sync
[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             if (ar[i].name == 'style') {
5486                throw "style removed?";
5487             }
5488             Roo.log("removeAttribute" + ar[i].name);
5489             from.removeAttribute(ar[i].name);
5490         }
5491         ar = to.attributes;
5492         for(var i = 0; i< ar.length;i++) {
5493             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5494                 continue;
5495             }
5496             Roo.log("updateAttribute " + from.getAttribute(ar[i].name) + '=>' + to.getAttribute(ar[i].name));
5497             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5498         }
5499         // children
5500         var far = Array.from(from.childNodes);
5501         var tar = Array.from(to.childNodes);
5502         // if the lengths are different.. then it's probably a editable content change, rather than
5503         // a change of the block definition..
5504         
5505         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5506          /*if (from.innerHTML == to.innerHTML) {
5507             return;
5508         }
5509         if (far.length != tar.length) {
5510             from.innerHTML = to.innerHTML;
5511             return;
5512         }
5513         */
5514         
5515         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5516             if (i >= far.length) {
5517                 from.appendChild(tar[i]);
5518                 Roo.log(["add", tar[i]]);
5519                 
5520             } else if ( i  >= tar.length) {
5521                 from.removeChild(far[i]);
5522                 Roo.log(["remove", far[i]]);
5523             } else {
5524                 
5525                 updateNode(far[i], tar[i]);
5526             }    
5527         }
5528         
5529         
5530         
5531         
5532     };
5533     
5534     
5535
5536     return {
5537         /** True to force the use of DOM instead of html fragments @type Boolean */
5538         useDom : false,
5539     
5540         /**
5541          * Returns the markup for the passed Element(s) config
5542          * @param {Object} o The Dom object spec (and children)
5543          * @return {String}
5544          */
5545         markup : function(o){
5546             return createHtml(o);
5547         },
5548     
5549         /**
5550          * Applies a style specification to an element
5551          * @param {String/HTMLElement} el The element to apply styles to
5552          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5553          * a function which returns such a specification.
5554          */
5555         applyStyles : function(el, styles){
5556             if(styles){
5557                el = Roo.fly(el);
5558                if(typeof styles == "string"){
5559                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5560                    var matches;
5561                    while ((matches = re.exec(styles)) != null){
5562                        el.setStyle(matches[1], matches[2]);
5563                    }
5564                }else if (typeof styles == "object"){
5565                    for (var style in styles){
5566                       el.setStyle(style, styles[style]);
5567                    }
5568                }else if (typeof styles == "function"){
5569                     Roo.DomHelper.applyStyles(el, styles.call());
5570                }
5571             }
5572         },
5573     
5574         /**
5575          * Inserts an HTML fragment into the Dom
5576          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5577          * @param {HTMLElement} el The context element
5578          * @param {String} html The HTML fragmenet
5579          * @return {HTMLElement} The new node
5580          */
5581         insertHtml : function(where, el, html){
5582             where = where.toLowerCase();
5583             if(el.insertAdjacentHTML){
5584                 if(tableRe.test(el.tagName)){
5585                     var rs;
5586                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5587                         return rs;
5588                     }
5589                 }
5590                 switch(where){
5591                     case "beforebegin":
5592                         el.insertAdjacentHTML('BeforeBegin', html);
5593                         return el.previousSibling;
5594                     case "afterbegin":
5595                         el.insertAdjacentHTML('AfterBegin', html);
5596                         return el.firstChild;
5597                     case "beforeend":
5598                         el.insertAdjacentHTML('BeforeEnd', html);
5599                         return el.lastChild;
5600                     case "afterend":
5601                         el.insertAdjacentHTML('AfterEnd', html);
5602                         return el.nextSibling;
5603                 }
5604                 throw 'Illegal insertion point -> "' + where + '"';
5605             }
5606             var range = el.ownerDocument.createRange();
5607             var frag;
5608             switch(where){
5609                  case "beforebegin":
5610                     range.setStartBefore(el);
5611                     frag = range.createContextualFragment(html);
5612                     el.parentNode.insertBefore(frag, el);
5613                     return el.previousSibling;
5614                  case "afterbegin":
5615                     if(el.firstChild){
5616                         range.setStartBefore(el.firstChild);
5617                         frag = range.createContextualFragment(html);
5618                         el.insertBefore(frag, el.firstChild);
5619                         return el.firstChild;
5620                     }else{
5621                         el.innerHTML = html;
5622                         return el.firstChild;
5623                     }
5624                 case "beforeend":
5625                     if(el.lastChild){
5626                         range.setStartAfter(el.lastChild);
5627                         frag = range.createContextualFragment(html);
5628                         el.appendChild(frag);
5629                         return el.lastChild;
5630                     }else{
5631                         el.innerHTML = html;
5632                         return el.lastChild;
5633                     }
5634                 case "afterend":
5635                     range.setStartAfter(el);
5636                     frag = range.createContextualFragment(html);
5637                     el.parentNode.insertBefore(frag, el.nextSibling);
5638                     return el.nextSibling;
5639                 }
5640                 throw 'Illegal insertion point -> "' + where + '"';
5641         },
5642     
5643         /**
5644          * Creates new Dom element(s) and inserts them before el
5645          * @param {String/HTMLElement/Element} el The context element
5646          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5647          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5648          * @return {HTMLElement/Roo.Element} The new node
5649          */
5650         insertBefore : function(el, o, returnElement){
5651             return this.doInsert(el, o, returnElement, "beforeBegin");
5652         },
5653     
5654         /**
5655          * Creates new Dom element(s) and inserts them after el
5656          * @param {String/HTMLElement/Element} el The context element
5657          * @param {Object} o The Dom object spec (and children)
5658          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5659          * @return {HTMLElement/Roo.Element} The new node
5660          */
5661         insertAfter : function(el, o, returnElement){
5662             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5663         },
5664     
5665         /**
5666          * Creates new Dom element(s) and inserts them as the first child of el
5667          * @param {String/HTMLElement/Element} el The context element
5668          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5669          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5670          * @return {HTMLElement/Roo.Element} The new node
5671          */
5672         insertFirst : function(el, o, returnElement){
5673             return this.doInsert(el, o, returnElement, "afterBegin");
5674         },
5675     
5676         // private
5677         doInsert : function(el, o, returnElement, pos, sibling){
5678             el = Roo.getDom(el);
5679             var newNode;
5680             if(this.useDom || o.ns){
5681                 newNode = createDom(o, null);
5682                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5683             }else{
5684                 var html = createHtml(o);
5685                 newNode = this.insertHtml(pos, el, html);
5686             }
5687             return returnElement ? Roo.get(newNode, true) : newNode;
5688         },
5689     
5690         /**
5691          * Creates new Dom element(s) and appends them to el
5692          * @param {String/HTMLElement/Element} el The context element
5693          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5694          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5695          * @return {HTMLElement/Roo.Element} The new node
5696          */
5697         append : function(el, o, returnElement){
5698             el = Roo.getDom(el);
5699             var newNode;
5700             if(this.useDom || o.ns){
5701                 newNode = createDom(o, null);
5702                 el.appendChild(newNode);
5703             }else{
5704                 var html = createHtml(o);
5705                 newNode = this.insertHtml("beforeEnd", el, html);
5706             }
5707             return returnElement ? Roo.get(newNode, true) : newNode;
5708         },
5709     
5710         /**
5711          * Creates new Dom element(s) and overwrites the contents of el with them
5712          * @param {String/HTMLElement/Element} el The context element
5713          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5714          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5715          * @return {HTMLElement/Roo.Element} The new node
5716          */
5717         overwrite : function(el, o, returnElement)
5718         {
5719             el = Roo.getDom(el);
5720             if (o.ns) {
5721               
5722                 while (el.childNodes.length) {
5723                     el.removeChild(el.firstChild);
5724                 }
5725                 createDom(o, el);
5726             } else {
5727                 el.innerHTML = createHtml(o);   
5728             }
5729             
5730             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5731         },
5732     
5733         /**
5734          * Creates a new Roo.DomHelper.Template from the Dom object spec
5735          * @param {Object} o The Dom object spec (and children)
5736          * @return {Roo.DomHelper.Template} The new template
5737          */
5738         createTemplate : function(o){
5739             var html = createHtml(o);
5740             return new Roo.Template(html);
5741         },
5742          /**
5743          * Updates the first element with the spec from the o (replacing if necessary)
5744          * This iterates through the children, and updates attributes / children etc..
5745          * @param {String/HTMLElement/Element} el The context element
5746          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5747          */
5748         
5749         update : function(el, o)
5750         {
5751             updateNode(Roo.getDom(el), createDom(o));
5752             
5753         }
5754         
5755         
5756     };
5757 }();
5758 /*
5759  * Based on:
5760  * Ext JS Library 1.1.1
5761  * Copyright(c) 2006-2007, Ext JS, LLC.
5762  *
5763  * Originally Released Under LGPL - original licence link has changed is not relivant.
5764  *
5765  * Fork - LGPL
5766  * <script type="text/javascript">
5767  */
5768  
5769 /**
5770 * @class Roo.Template
5771 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5772 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5773 * Usage:
5774 <pre><code>
5775 var t = new Roo.Template({
5776     html :  '&lt;div name="{id}"&gt;' + 
5777         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5778         '&lt;/div&gt;',
5779     myformat: function (value, allValues) {
5780         return 'XX' + value;
5781     }
5782 });
5783 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5784 </code></pre>
5785 * For more information see this blog post with examples:
5786 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5787      - Create Elements using DOM, HTML fragments and Templates</a>. 
5788 * @constructor
5789 * @param {Object} cfg - Configuration object.
5790 */
5791 Roo.Template = function(cfg){
5792     // BC!
5793     if(cfg instanceof Array){
5794         cfg = cfg.join("");
5795     }else if(arguments.length > 1){
5796         cfg = Array.prototype.join.call(arguments, "");
5797     }
5798     
5799     
5800     if (typeof(cfg) == 'object') {
5801         Roo.apply(this,cfg)
5802     } else {
5803         // bc
5804         this.html = cfg;
5805     }
5806     if (this.url) {
5807         this.load();
5808     }
5809     
5810 };
5811 Roo.Template.prototype = {
5812     
5813     /**
5814      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5815      */
5816     onLoad : false,
5817     
5818     
5819     /**
5820      * @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..
5821      *                    it should be fixed so that template is observable...
5822      */
5823     url : false,
5824     /**
5825      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5826      */
5827     html : '',
5828     
5829     
5830     compiled : false,
5831     loaded : false,
5832     /**
5833      * Returns an HTML fragment of this template with the specified values applied.
5834      * @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'})
5835      * @return {String} The HTML fragment
5836      */
5837     
5838    
5839     
5840     applyTemplate : function(values){
5841         //Roo.log(["applyTemplate", values]);
5842         try {
5843            
5844             if(this.compiled){
5845                 return this.compiled(values);
5846             }
5847             var useF = this.disableFormats !== true;
5848             var fm = Roo.util.Format, tpl = this;
5849             var fn = function(m, name, format, args){
5850                 if(format && useF){
5851                     if(format.substr(0, 5) == "this."){
5852                         return tpl.call(format.substr(5), values[name], values);
5853                     }else{
5854                         if(args){
5855                             // quoted values are required for strings in compiled templates, 
5856                             // but for non compiled we need to strip them
5857                             // quoted reversed for jsmin
5858                             var re = /^\s*['"](.*)["']\s*$/;
5859                             args = args.split(',');
5860                             for(var i = 0, len = args.length; i < len; i++){
5861                                 args[i] = args[i].replace(re, "$1");
5862                             }
5863                             args = [values[name]].concat(args);
5864                         }else{
5865                             args = [values[name]];
5866                         }
5867                         return fm[format].apply(fm, args);
5868                     }
5869                 }else{
5870                     return values[name] !== undefined ? values[name] : "";
5871                 }
5872             };
5873             return this.html.replace(this.re, fn);
5874         } catch (e) {
5875             Roo.log(e);
5876             throw e;
5877         }
5878          
5879     },
5880     
5881     loading : false,
5882       
5883     load : function ()
5884     {
5885          
5886         if (this.loading) {
5887             return;
5888         }
5889         var _t = this;
5890         
5891         this.loading = true;
5892         this.compiled = false;
5893         
5894         var cx = new Roo.data.Connection();
5895         cx.request({
5896             url : this.url,
5897             method : 'GET',
5898             success : function (response) {
5899                 _t.loading = false;
5900                 _t.url = false;
5901                 
5902                 _t.set(response.responseText,true);
5903                 _t.loaded = true;
5904                 if (_t.onLoad) {
5905                     _t.onLoad();
5906                 }
5907              },
5908             failure : function(response) {
5909                 Roo.log("Template failed to load from " + _t.url);
5910                 _t.loading = false;
5911             }
5912         });
5913     },
5914
5915     /**
5916      * Sets the HTML used as the template and optionally compiles it.
5917      * @param {String} html
5918      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5919      * @return {Roo.Template} this
5920      */
5921     set : function(html, compile){
5922         this.html = html;
5923         this.compiled = false;
5924         if(compile){
5925             this.compile();
5926         }
5927         return this;
5928     },
5929     
5930     /**
5931      * True to disable format functions (defaults to false)
5932      * @type Boolean
5933      */
5934     disableFormats : false,
5935     
5936     /**
5937     * The regular expression used to match template variables 
5938     * @type RegExp
5939     * @property 
5940     */
5941     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5942     
5943     /**
5944      * Compiles the template into an internal function, eliminating the RegEx overhead.
5945      * @return {Roo.Template} this
5946      */
5947     compile : function(){
5948         var fm = Roo.util.Format;
5949         var useF = this.disableFormats !== true;
5950         var sep = Roo.isGecko ? "+" : ",";
5951         var fn = function(m, name, format, args){
5952             if(format && useF){
5953                 args = args ? ',' + args : "";
5954                 if(format.substr(0, 5) != "this."){
5955                     format = "fm." + format + '(';
5956                 }else{
5957                     format = 'this.call("'+ format.substr(5) + '", ';
5958                     args = ", values";
5959                 }
5960             }else{
5961                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5962             }
5963             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5964         };
5965         var body;
5966         // branched to use + in gecko and [].join() in others
5967         if(Roo.isGecko){
5968             body = "this.compiled = function(values){ return '" +
5969                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5970                     "';};";
5971         }else{
5972             body = ["this.compiled = function(values){ return ['"];
5973             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5974             body.push("'].join('');};");
5975             body = body.join('');
5976         }
5977         /**
5978          * eval:var:values
5979          * eval:var:fm
5980          */
5981         eval(body);
5982         return this;
5983     },
5984     
5985     // private function used to call members
5986     call : function(fnName, value, allValues){
5987         return this[fnName](value, allValues);
5988     },
5989     
5990     /**
5991      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5992      * @param {String/HTMLElement/Roo.Element} el The context element
5993      * @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'})
5994      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5995      * @return {HTMLElement/Roo.Element} The new node or Element
5996      */
5997     insertFirst: function(el, values, returnElement){
5998         return this.doInsert('afterBegin', el, values, returnElement);
5999     },
6000
6001     /**
6002      * Applies the supplied values to the template and inserts the new node(s) before el.
6003      * @param {String/HTMLElement/Roo.Element} el The context element
6004      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6005      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6006      * @return {HTMLElement/Roo.Element} The new node or Element
6007      */
6008     insertBefore: function(el, values, returnElement){
6009         return this.doInsert('beforeBegin', el, values, returnElement);
6010     },
6011
6012     /**
6013      * Applies the supplied values to the template and inserts the new node(s) after el.
6014      * @param {String/HTMLElement/Roo.Element} el The context element
6015      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6016      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6017      * @return {HTMLElement/Roo.Element} The new node or Element
6018      */
6019     insertAfter : function(el, values, returnElement){
6020         return this.doInsert('afterEnd', el, values, returnElement);
6021     },
6022     
6023     /**
6024      * Applies the supplied values to the template and appends the new node(s) to el.
6025      * @param {String/HTMLElement/Roo.Element} el The context element
6026      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6027      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6028      * @return {HTMLElement/Roo.Element} The new node or Element
6029      */
6030     append : function(el, values, returnElement){
6031         return this.doInsert('beforeEnd', el, values, returnElement);
6032     },
6033
6034     doInsert : function(where, el, values, returnEl){
6035         el = Roo.getDom(el);
6036         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6037         return returnEl ? Roo.get(newNode, true) : newNode;
6038     },
6039
6040     /**
6041      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6042      * @param {String/HTMLElement/Roo.Element} el The context element
6043      * @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'})
6044      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6045      * @return {HTMLElement/Roo.Element} The new node or Element
6046      */
6047     overwrite : function(el, values, returnElement){
6048         el = Roo.getDom(el);
6049         el.innerHTML = this.applyTemplate(values);
6050         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6051     }
6052 };
6053 /**
6054  * Alias for {@link #applyTemplate}
6055  * @method
6056  */
6057 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6058
6059 // backwards compat
6060 Roo.DomHelper.Template = Roo.Template;
6061
6062 /**
6063  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6064  * @param {String/HTMLElement} el A DOM element or its id
6065  * @returns {Roo.Template} The created template
6066  * @static
6067  */
6068 Roo.Template.from = function(el){
6069     el = Roo.getDom(el);
6070     return new Roo.Template(el.value || el.innerHTML);
6071 };/*
6072  * Based on:
6073  * Ext JS Library 1.1.1
6074  * Copyright(c) 2006-2007, Ext JS, LLC.
6075  *
6076  * Originally Released Under LGPL - original licence link has changed is not relivant.
6077  *
6078  * Fork - LGPL
6079  * <script type="text/javascript">
6080  */
6081  
6082
6083 /*
6084  * This is code is also distributed under MIT license for use
6085  * with jQuery and prototype JavaScript libraries.
6086  */
6087 /**
6088  * @class Roo.DomQuery
6089 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).
6090 <p>
6091 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>
6092
6093 <p>
6094 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.
6095 </p>
6096 <h4>Element Selectors:</h4>
6097 <ul class="list">
6098     <li> <b>*</b> any element</li>
6099     <li> <b>E</b> an element with the tag E</li>
6100     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6101     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6102     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6103     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6104 </ul>
6105 <h4>Attribute Selectors:</h4>
6106 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6107 <ul class="list">
6108     <li> <b>E[foo]</b> has an attribute "foo"</li>
6109     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6110     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6111     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6112     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6113     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6114     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6115 </ul>
6116 <h4>Pseudo Classes:</h4>
6117 <ul class="list">
6118     <li> <b>E:first-child</b> E is the first child of its parent</li>
6119     <li> <b>E:last-child</b> E is the last child of its parent</li>
6120     <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>
6121     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6122     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6123     <li> <b>E:only-child</b> E is the only child of its parent</li>
6124     <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>
6125     <li> <b>E:first</b> the first E in the resultset</li>
6126     <li> <b>E:last</b> the last E in the resultset</li>
6127     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6128     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6129     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6130     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6131     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6132     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6133     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6134     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6135     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6136 </ul>
6137 <h4>CSS Value Selectors:</h4>
6138 <ul class="list">
6139     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6140     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6141     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6142     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6143     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6144     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6145 </ul>
6146  * @static
6147  */
6148 Roo.DomQuery = function(){
6149     var cache = {}, simpleCache = {}, valueCache = {};
6150     var nonSpace = /\S/;
6151     var trimRe = /^\s+|\s+$/g;
6152     var tplRe = /\{(\d+)\}/g;
6153     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6154     var tagTokenRe = /^(#)?([\w-\*]+)/;
6155     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6156
6157     function child(p, index){
6158         var i = 0;
6159         var n = p.firstChild;
6160         while(n){
6161             if(n.nodeType == 1){
6162                if(++i == index){
6163                    return n;
6164                }
6165             }
6166             n = n.nextSibling;
6167         }
6168         return null;
6169     };
6170
6171     function next(n){
6172         while((n = n.nextSibling) && n.nodeType != 1);
6173         return n;
6174     };
6175
6176     function prev(n){
6177         while((n = n.previousSibling) && n.nodeType != 1);
6178         return n;
6179     };
6180
6181     function children(d){
6182         var n = d.firstChild, ni = -1;
6183             while(n){
6184                 var nx = n.nextSibling;
6185                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6186                     d.removeChild(n);
6187                 }else{
6188                     n.nodeIndex = ++ni;
6189                 }
6190                 n = nx;
6191             }
6192             return this;
6193         };
6194
6195     function byClassName(c, a, v){
6196         if(!v){
6197             return c;
6198         }
6199         var r = [], ri = -1, cn;
6200         for(var i = 0, ci; ci = c[i]; i++){
6201             
6202             
6203             if((' '+
6204                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6205                  +' ').indexOf(v) != -1){
6206                 r[++ri] = ci;
6207             }
6208         }
6209         return r;
6210     };
6211
6212     function attrValue(n, attr){
6213         if(!n.tagName && typeof n.length != "undefined"){
6214             n = n[0];
6215         }
6216         if(!n){
6217             return null;
6218         }
6219         if(attr == "for"){
6220             return n.htmlFor;
6221         }
6222         if(attr == "class" || attr == "className"){
6223             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6224         }
6225         return n.getAttribute(attr) || n[attr];
6226
6227     };
6228
6229     function getNodes(ns, mode, tagName){
6230         var result = [], ri = -1, cs;
6231         if(!ns){
6232             return result;
6233         }
6234         tagName = tagName || "*";
6235         if(typeof ns.getElementsByTagName != "undefined"){
6236             ns = [ns];
6237         }
6238         if(!mode){
6239             for(var i = 0, ni; ni = ns[i]; i++){
6240                 cs = ni.getElementsByTagName(tagName);
6241                 for(var j = 0, ci; ci = cs[j]; j++){
6242                     result[++ri] = ci;
6243                 }
6244             }
6245         }else if(mode == "/" || mode == ">"){
6246             var utag = tagName.toUpperCase();
6247             for(var i = 0, ni, cn; ni = ns[i]; i++){
6248                 cn = ni.children || ni.childNodes;
6249                 for(var j = 0, cj; cj = cn[j]; j++){
6250                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6251                         result[++ri] = cj;
6252                     }
6253                 }
6254             }
6255         }else if(mode == "+"){
6256             var utag = tagName.toUpperCase();
6257             for(var i = 0, n; n = ns[i]; i++){
6258                 while((n = n.nextSibling) && n.nodeType != 1);
6259                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6260                     result[++ri] = n;
6261                 }
6262             }
6263         }else if(mode == "~"){
6264             for(var i = 0, n; n = ns[i]; i++){
6265                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6266                 if(n){
6267                     result[++ri] = n;
6268                 }
6269             }
6270         }
6271         return result;
6272     };
6273
6274     function concat(a, b){
6275         if(b.slice){
6276             return a.concat(b);
6277         }
6278         for(var i = 0, l = b.length; i < l; i++){
6279             a[a.length] = b[i];
6280         }
6281         return a;
6282     }
6283
6284     function byTag(cs, tagName){
6285         if(cs.tagName || cs == document){
6286             cs = [cs];
6287         }
6288         if(!tagName){
6289             return cs;
6290         }
6291         var r = [], ri = -1;
6292         tagName = tagName.toLowerCase();
6293         for(var i = 0, ci; ci = cs[i]; i++){
6294             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6295                 r[++ri] = ci;
6296             }
6297         }
6298         return r;
6299     };
6300
6301     function byId(cs, attr, id){
6302         if(cs.tagName || cs == document){
6303             cs = [cs];
6304         }
6305         if(!id){
6306             return cs;
6307         }
6308         var r = [], ri = -1;
6309         for(var i = 0,ci; ci = cs[i]; i++){
6310             if(ci && ci.id == id){
6311                 r[++ri] = ci;
6312                 return r;
6313             }
6314         }
6315         return r;
6316     };
6317
6318     function byAttribute(cs, attr, value, op, custom){
6319         var r = [], ri = -1, st = custom=="{";
6320         var f = Roo.DomQuery.operators[op];
6321         for(var i = 0, ci; ci = cs[i]; i++){
6322             var a;
6323             if(st){
6324                 a = Roo.DomQuery.getStyle(ci, attr);
6325             }
6326             else if(attr == "class" || attr == "className"){
6327                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6328             }else if(attr == "for"){
6329                 a = ci.htmlFor;
6330             }else if(attr == "href"){
6331                 a = ci.getAttribute("href", 2);
6332             }else{
6333                 a = ci.getAttribute(attr);
6334             }
6335             if((f && f(a, value)) || (!f && a)){
6336                 r[++ri] = ci;
6337             }
6338         }
6339         return r;
6340     };
6341
6342     function byPseudo(cs, name, value){
6343         return Roo.DomQuery.pseudos[name](cs, value);
6344     };
6345
6346     // This is for IE MSXML which does not support expandos.
6347     // IE runs the same speed using setAttribute, however FF slows way down
6348     // and Safari completely fails so they need to continue to use expandos.
6349     var isIE = window.ActiveXObject ? true : false;
6350
6351     // this eval is stop the compressor from
6352     // renaming the variable to something shorter
6353     
6354     /** eval:var:batch */
6355     var batch = 30803; 
6356
6357     var key = 30803;
6358
6359     function nodupIEXml(cs){
6360         var d = ++key;
6361         cs[0].setAttribute("_nodup", d);
6362         var r = [cs[0]];
6363         for(var i = 1, len = cs.length; i < len; i++){
6364             var c = cs[i];
6365             if(!c.getAttribute("_nodup") != d){
6366                 c.setAttribute("_nodup", d);
6367                 r[r.length] = c;
6368             }
6369         }
6370         for(var i = 0, len = cs.length; i < len; i++){
6371             cs[i].removeAttribute("_nodup");
6372         }
6373         return r;
6374     }
6375
6376     function nodup(cs){
6377         if(!cs){
6378             return [];
6379         }
6380         var len = cs.length, c, i, r = cs, cj, ri = -1;
6381         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6382             return cs;
6383         }
6384         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6385             return nodupIEXml(cs);
6386         }
6387         var d = ++key;
6388         cs[0]._nodup = d;
6389         for(i = 1; c = cs[i]; i++){
6390             if(c._nodup != d){
6391                 c._nodup = d;
6392             }else{
6393                 r = [];
6394                 for(var j = 0; j < i; j++){
6395                     r[++ri] = cs[j];
6396                 }
6397                 for(j = i+1; cj = cs[j]; j++){
6398                     if(cj._nodup != d){
6399                         cj._nodup = d;
6400                         r[++ri] = cj;
6401                     }
6402                 }
6403                 return r;
6404             }
6405         }
6406         return r;
6407     }
6408
6409     function quickDiffIEXml(c1, c2){
6410         var d = ++key;
6411         for(var i = 0, len = c1.length; i < len; i++){
6412             c1[i].setAttribute("_qdiff", d);
6413         }
6414         var r = [];
6415         for(var i = 0, len = c2.length; i < len; i++){
6416             if(c2[i].getAttribute("_qdiff") != d){
6417                 r[r.length] = c2[i];
6418             }
6419         }
6420         for(var i = 0, len = c1.length; i < len; i++){
6421            c1[i].removeAttribute("_qdiff");
6422         }
6423         return r;
6424     }
6425
6426     function quickDiff(c1, c2){
6427         var len1 = c1.length;
6428         if(!len1){
6429             return c2;
6430         }
6431         if(isIE && c1[0].selectSingleNode){
6432             return quickDiffIEXml(c1, c2);
6433         }
6434         var d = ++key;
6435         for(var i = 0; i < len1; i++){
6436             c1[i]._qdiff = d;
6437         }
6438         var r = [];
6439         for(var i = 0, len = c2.length; i < len; i++){
6440             if(c2[i]._qdiff != d){
6441                 r[r.length] = c2[i];
6442             }
6443         }
6444         return r;
6445     }
6446
6447     function quickId(ns, mode, root, id){
6448         if(ns == root){
6449            var d = root.ownerDocument || root;
6450            return d.getElementById(id);
6451         }
6452         ns = getNodes(ns, mode, "*");
6453         return byId(ns, null, id);
6454     }
6455
6456     return {
6457         getStyle : function(el, name){
6458             return Roo.fly(el).getStyle(name);
6459         },
6460         /**
6461          * Compiles a selector/xpath query into a reusable function. The returned function
6462          * takes one parameter "root" (optional), which is the context node from where the query should start.
6463          * @param {String} selector The selector/xpath query
6464          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6465          * @return {Function}
6466          */
6467         compile : function(path, type){
6468             type = type || "select";
6469             
6470             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6471             var q = path, mode, lq;
6472             var tk = Roo.DomQuery.matchers;
6473             var tklen = tk.length;
6474             var mm;
6475
6476             // accept leading mode switch
6477             var lmode = q.match(modeRe);
6478             if(lmode && lmode[1]){
6479                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6480                 q = q.replace(lmode[1], "");
6481             }
6482             // strip leading slashes
6483             while(path.substr(0, 1)=="/"){
6484                 path = path.substr(1);
6485             }
6486
6487             while(q && lq != q){
6488                 lq = q;
6489                 var tm = q.match(tagTokenRe);
6490                 if(type == "select"){
6491                     if(tm){
6492                         if(tm[1] == "#"){
6493                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6494                         }else{
6495                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6496                         }
6497                         q = q.replace(tm[0], "");
6498                     }else if(q.substr(0, 1) != '@'){
6499                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6500                     }
6501                 }else{
6502                     if(tm){
6503                         if(tm[1] == "#"){
6504                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6505                         }else{
6506                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6507                         }
6508                         q = q.replace(tm[0], "");
6509                     }
6510                 }
6511                 while(!(mm = q.match(modeRe))){
6512                     var matched = false;
6513                     for(var j = 0; j < tklen; j++){
6514                         var t = tk[j];
6515                         var m = q.match(t.re);
6516                         if(m){
6517                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6518                                                     return m[i];
6519                                                 });
6520                             q = q.replace(m[0], "");
6521                             matched = true;
6522                             break;
6523                         }
6524                     }
6525                     // prevent infinite loop on bad selector
6526                     if(!matched){
6527                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6528                     }
6529                 }
6530                 if(mm[1]){
6531                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6532                     q = q.replace(mm[1], "");
6533                 }
6534             }
6535             fn[fn.length] = "return nodup(n);\n}";
6536             
6537              /** 
6538               * list of variables that need from compression as they are used by eval.
6539              *  eval:var:batch 
6540              *  eval:var:nodup
6541              *  eval:var:byTag
6542              *  eval:var:ById
6543              *  eval:var:getNodes
6544              *  eval:var:quickId
6545              *  eval:var:mode
6546              *  eval:var:root
6547              *  eval:var:n
6548              *  eval:var:byClassName
6549              *  eval:var:byPseudo
6550              *  eval:var:byAttribute
6551              *  eval:var:attrValue
6552              * 
6553              **/ 
6554             eval(fn.join(""));
6555             return f;
6556         },
6557
6558         /**
6559          * Selects a group of elements.
6560          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6561          * @param {Node} root (optional) The start of the query (defaults to document).
6562          * @return {Array}
6563          */
6564         select : function(path, root, type){
6565             if(!root || root == document){
6566                 root = document;
6567             }
6568             if(typeof root == "string"){
6569                 root = document.getElementById(root);
6570             }
6571             var paths = path.split(",");
6572             var results = [];
6573             for(var i = 0, len = paths.length; i < len; i++){
6574                 var p = paths[i].replace(trimRe, "");
6575                 if(!cache[p]){
6576                     cache[p] = Roo.DomQuery.compile(p);
6577                     if(!cache[p]){
6578                         throw p + " is not a valid selector";
6579                     }
6580                 }
6581                 var result = cache[p](root);
6582                 if(result && result != document){
6583                     results = results.concat(result);
6584                 }
6585             }
6586             if(paths.length > 1){
6587                 return nodup(results);
6588             }
6589             return results;
6590         },
6591
6592         /**
6593          * Selects a single element.
6594          * @param {String} selector The selector/xpath query
6595          * @param {Node} root (optional) The start of the query (defaults to document).
6596          * @return {Element}
6597          */
6598         selectNode : function(path, root){
6599             return Roo.DomQuery.select(path, root)[0];
6600         },
6601
6602         /**
6603          * Selects the value of a node, optionally replacing null with the defaultValue.
6604          * @param {String} selector The selector/xpath query
6605          * @param {Node} root (optional) The start of the query (defaults to document).
6606          * @param {String} defaultValue
6607          */
6608         selectValue : function(path, root, defaultValue){
6609             path = path.replace(trimRe, "");
6610             if(!valueCache[path]){
6611                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6612             }
6613             var n = valueCache[path](root);
6614             n = n[0] ? n[0] : n;
6615             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6616             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6617         },
6618
6619         /**
6620          * Selects the value of a node, parsing integers and floats.
6621          * @param {String} selector The selector/xpath query
6622          * @param {Node} root (optional) The start of the query (defaults to document).
6623          * @param {Number} defaultValue
6624          * @return {Number}
6625          */
6626         selectNumber : function(path, root, defaultValue){
6627             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6628             return parseFloat(v);
6629         },
6630
6631         /**
6632          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6633          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6634          * @param {String} selector The simple selector to test
6635          * @return {Boolean}
6636          */
6637         is : function(el, ss){
6638             if(typeof el == "string"){
6639                 el = document.getElementById(el);
6640             }
6641             var isArray = (el instanceof Array);
6642             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6643             return isArray ? (result.length == el.length) : (result.length > 0);
6644         },
6645
6646         /**
6647          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6648          * @param {Array} el An array of elements to filter
6649          * @param {String} selector The simple selector to test
6650          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6651          * the selector instead of the ones that match
6652          * @return {Array}
6653          */
6654         filter : function(els, ss, nonMatches){
6655             ss = ss.replace(trimRe, "");
6656             if(!simpleCache[ss]){
6657                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6658             }
6659             var result = simpleCache[ss](els);
6660             return nonMatches ? quickDiff(result, els) : result;
6661         },
6662
6663         /**
6664          * Collection of matching regular expressions and code snippets.
6665          */
6666         matchers : [{
6667                 re: /^\.([\w-]+)/,
6668                 select: 'n = byClassName(n, null, " {1} ");'
6669             }, {
6670                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6671                 select: 'n = byPseudo(n, "{1}", "{2}");'
6672             },{
6673                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6674                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6675             }, {
6676                 re: /^#([\w-]+)/,
6677                 select: 'n = byId(n, null, "{1}");'
6678             },{
6679                 re: /^@([\w-]+)/,
6680                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6681             }
6682         ],
6683
6684         /**
6685          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6686          * 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;.
6687          */
6688         operators : {
6689             "=" : function(a, v){
6690                 return a == v;
6691             },
6692             "!=" : function(a, v){
6693                 return a != v;
6694             },
6695             "^=" : function(a, v){
6696                 return a && a.substr(0, v.length) == v;
6697             },
6698             "$=" : function(a, v){
6699                 return a && a.substr(a.length-v.length) == v;
6700             },
6701             "*=" : function(a, v){
6702                 return a && a.indexOf(v) !== -1;
6703             },
6704             "%=" : function(a, v){
6705                 return (a % v) == 0;
6706             },
6707             "|=" : function(a, v){
6708                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6709             },
6710             "~=" : function(a, v){
6711                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6712             }
6713         },
6714
6715         /**
6716          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6717          * and the argument (if any) supplied in the selector.
6718          */
6719         pseudos : {
6720             "first-child" : function(c){
6721                 var r = [], ri = -1, n;
6722                 for(var i = 0, ci; ci = n = c[i]; i++){
6723                     while((n = n.previousSibling) && n.nodeType != 1);
6724                     if(!n){
6725                         r[++ri] = ci;
6726                     }
6727                 }
6728                 return r;
6729             },
6730
6731             "last-child" : function(c){
6732                 var r = [], ri = -1, n;
6733                 for(var i = 0, ci; ci = n = c[i]; i++){
6734                     while((n = n.nextSibling) && n.nodeType != 1);
6735                     if(!n){
6736                         r[++ri] = ci;
6737                     }
6738                 }
6739                 return r;
6740             },
6741
6742             "nth-child" : function(c, a) {
6743                 var r = [], ri = -1;
6744                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6745                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6746                 for(var i = 0, n; n = c[i]; i++){
6747                     var pn = n.parentNode;
6748                     if (batch != pn._batch) {
6749                         var j = 0;
6750                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6751                             if(cn.nodeType == 1){
6752                                cn.nodeIndex = ++j;
6753                             }
6754                         }
6755                         pn._batch = batch;
6756                     }
6757                     if (f == 1) {
6758                         if (l == 0 || n.nodeIndex == l){
6759                             r[++ri] = n;
6760                         }
6761                     } else if ((n.nodeIndex + l) % f == 0){
6762                         r[++ri] = n;
6763                     }
6764                 }
6765
6766                 return r;
6767             },
6768
6769             "only-child" : function(c){
6770                 var r = [], ri = -1;;
6771                 for(var i = 0, ci; ci = c[i]; i++){
6772                     if(!prev(ci) && !next(ci)){
6773                         r[++ri] = ci;
6774                     }
6775                 }
6776                 return r;
6777             },
6778
6779             "empty" : function(c){
6780                 var r = [], ri = -1;
6781                 for(var i = 0, ci; ci = c[i]; i++){
6782                     var cns = ci.childNodes, j = 0, cn, empty = true;
6783                     while(cn = cns[j]){
6784                         ++j;
6785                         if(cn.nodeType == 1 || cn.nodeType == 3){
6786                             empty = false;
6787                             break;
6788                         }
6789                     }
6790                     if(empty){
6791                         r[++ri] = ci;
6792                     }
6793                 }
6794                 return r;
6795             },
6796
6797             "contains" : function(c, v){
6798                 var r = [], ri = -1;
6799                 for(var i = 0, ci; ci = c[i]; i++){
6800                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6801                         r[++ri] = ci;
6802                     }
6803                 }
6804                 return r;
6805             },
6806
6807             "nodeValue" : function(c, v){
6808                 var r = [], ri = -1;
6809                 for(var i = 0, ci; ci = c[i]; i++){
6810                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6811                         r[++ri] = ci;
6812                     }
6813                 }
6814                 return r;
6815             },
6816
6817             "checked" : function(c){
6818                 var r = [], ri = -1;
6819                 for(var i = 0, ci; ci = c[i]; i++){
6820                     if(ci.checked == true){
6821                         r[++ri] = ci;
6822                     }
6823                 }
6824                 return r;
6825             },
6826
6827             "not" : function(c, ss){
6828                 return Roo.DomQuery.filter(c, ss, true);
6829             },
6830
6831             "odd" : function(c){
6832                 return this["nth-child"](c, "odd");
6833             },
6834
6835             "even" : function(c){
6836                 return this["nth-child"](c, "even");
6837             },
6838
6839             "nth" : function(c, a){
6840                 return c[a-1] || [];
6841             },
6842
6843             "first" : function(c){
6844                 return c[0] || [];
6845             },
6846
6847             "last" : function(c){
6848                 return c[c.length-1] || [];
6849             },
6850
6851             "has" : function(c, ss){
6852                 var s = Roo.DomQuery.select;
6853                 var r = [], ri = -1;
6854                 for(var i = 0, ci; ci = c[i]; i++){
6855                     if(s(ss, ci).length > 0){
6856                         r[++ri] = ci;
6857                     }
6858                 }
6859                 return r;
6860             },
6861
6862             "next" : function(c, ss){
6863                 var is = Roo.DomQuery.is;
6864                 var r = [], ri = -1;
6865                 for(var i = 0, ci; ci = c[i]; i++){
6866                     var n = next(ci);
6867                     if(n && is(n, ss)){
6868                         r[++ri] = ci;
6869                     }
6870                 }
6871                 return r;
6872             },
6873
6874             "prev" : function(c, ss){
6875                 var is = Roo.DomQuery.is;
6876                 var r = [], ri = -1;
6877                 for(var i = 0, ci; ci = c[i]; i++){
6878                     var n = prev(ci);
6879                     if(n && is(n, ss)){
6880                         r[++ri] = ci;
6881                     }
6882                 }
6883                 return r;
6884             }
6885         }
6886     };
6887 }();
6888
6889 /**
6890  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6891  * @param {String} path The selector/xpath query
6892  * @param {Node} root (optional) The start of the query (defaults to document).
6893  * @return {Array}
6894  * @member Roo
6895  * @method query
6896  */
6897 Roo.query = Roo.DomQuery.select;
6898 /*
6899  * Based on:
6900  * Ext JS Library 1.1.1
6901  * Copyright(c) 2006-2007, Ext JS, LLC.
6902  *
6903  * Originally Released Under LGPL - original licence link has changed is not relivant.
6904  *
6905  * Fork - LGPL
6906  * <script type="text/javascript">
6907  */
6908
6909 /**
6910  * @class Roo.util.Observable
6911  * Base class that provides a common interface for publishing events. Subclasses are expected to
6912  * to have a property "events" with all the events defined.<br>
6913  * For example:
6914  * <pre><code>
6915  Employee = function(name){
6916     this.name = name;
6917     this.addEvents({
6918         "fired" : true,
6919         "quit" : true
6920     });
6921  }
6922  Roo.extend(Employee, Roo.util.Observable);
6923 </code></pre>
6924  * @param {Object} config properties to use (incuding events / listeners)
6925  */
6926
6927 Roo.util.Observable = function(cfg){
6928     
6929     cfg = cfg|| {};
6930     this.addEvents(cfg.events || {});
6931     if (cfg.events) {
6932         delete cfg.events; // make sure
6933     }
6934      
6935     Roo.apply(this, cfg);
6936     
6937     if(this.listeners){
6938         this.on(this.listeners);
6939         delete this.listeners;
6940     }
6941 };
6942 Roo.util.Observable.prototype = {
6943     /** 
6944  * @cfg {Object} listeners  list of events and functions to call for this object, 
6945  * For example :
6946  * <pre><code>
6947     listeners :  { 
6948        'click' : function(e) {
6949            ..... 
6950         } ,
6951         .... 
6952     } 
6953   </code></pre>
6954  */
6955     
6956     
6957     /**
6958      * Fires the specified event with the passed parameters (minus the event name).
6959      * @param {String} eventName
6960      * @param {Object...} args Variable number of parameters are passed to handlers
6961      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6962      */
6963     fireEvent : function(){
6964         var ce = this.events[arguments[0].toLowerCase()];
6965         if(typeof ce == "object"){
6966             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6967         }else{
6968             return true;
6969         }
6970     },
6971
6972     // private
6973     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6974
6975     /**
6976      * Appends an event handler to this component
6977      * @param {String}   eventName The type of event to listen for
6978      * @param {Function} handler The method the event invokes
6979      * @param {Object}   scope (optional) The scope in which to execute the handler
6980      * function. The handler function's "this" context.
6981      * @param {Object}   options (optional) An object containing handler configuration
6982      * properties. This may contain any of the following properties:<ul>
6983      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6984      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6985      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6986      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6987      * by the specified number of milliseconds. If the event fires again within that time, the original
6988      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6989      * </ul><br>
6990      * <p>
6991      * <b>Combining Options</b><br>
6992      * Using the options argument, it is possible to combine different types of listeners:<br>
6993      * <br>
6994      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6995                 <pre><code>
6996                 el.on('click', this.onClick, this, {
6997                         single: true,
6998                 delay: 100,
6999                 forumId: 4
7000                 });
7001                 </code></pre>
7002      * <p>
7003      * <b>Attaching multiple handlers in 1 call</b><br>
7004      * The method also allows for a single argument to be passed which is a config object containing properties
7005      * which specify multiple handlers.
7006      * <pre><code>
7007                 el.on({
7008                         'click': {
7009                         fn: this.onClick,
7010                         scope: this,
7011                         delay: 100
7012                 }, 
7013                 'mouseover': {
7014                         fn: this.onMouseOver,
7015                         scope: this
7016                 },
7017                 'mouseout': {
7018                         fn: this.onMouseOut,
7019                         scope: this
7020                 }
7021                 });
7022                 </code></pre>
7023      * <p>
7024      * Or a shorthand syntax which passes the same scope object to all handlers:
7025         <pre><code>
7026                 el.on({
7027                         'click': this.onClick,
7028                 'mouseover': this.onMouseOver,
7029                 'mouseout': this.onMouseOut,
7030                 scope: this
7031                 });
7032                 </code></pre>
7033      */
7034     addListener : function(eventName, fn, scope, o){
7035         if(typeof eventName == "object"){
7036             o = eventName;
7037             for(var e in o){
7038                 if(this.filterOptRe.test(e)){
7039                     continue;
7040                 }
7041                 if(typeof o[e] == "function"){
7042                     // shared options
7043                     this.addListener(e, o[e], o.scope,  o);
7044                 }else{
7045                     // individual options
7046                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7047                 }
7048             }
7049             return;
7050         }
7051         o = (!o || typeof o == "boolean") ? {} : o;
7052         eventName = eventName.toLowerCase();
7053         var ce = this.events[eventName] || true;
7054         if(typeof ce == "boolean"){
7055             ce = new Roo.util.Event(this, eventName);
7056             this.events[eventName] = ce;
7057         }
7058         ce.addListener(fn, scope, o);
7059     },
7060
7061     /**
7062      * Removes a listener
7063      * @param {String}   eventName     The type of event to listen for
7064      * @param {Function} handler        The handler to remove
7065      * @param {Object}   scope  (optional) The scope (this object) for the handler
7066      */
7067     removeListener : function(eventName, fn, scope){
7068         var ce = this.events[eventName.toLowerCase()];
7069         if(typeof ce == "object"){
7070             ce.removeListener(fn, scope);
7071         }
7072     },
7073
7074     /**
7075      * Removes all listeners for this object
7076      */
7077     purgeListeners : function(){
7078         for(var evt in this.events){
7079             if(typeof this.events[evt] == "object"){
7080                  this.events[evt].clearListeners();
7081             }
7082         }
7083     },
7084
7085     relayEvents : function(o, events){
7086         var createHandler = function(ename){
7087             return function(){
7088                  
7089                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7090             };
7091         };
7092         for(var i = 0, len = events.length; i < len; i++){
7093             var ename = events[i];
7094             if(!this.events[ename]){
7095                 this.events[ename] = true;
7096             };
7097             o.on(ename, createHandler(ename), this);
7098         }
7099     },
7100
7101     /**
7102      * Used to define events on this Observable
7103      * @param {Object} object The object with the events defined
7104      */
7105     addEvents : function(o){
7106         if(!this.events){
7107             this.events = {};
7108         }
7109         Roo.applyIf(this.events, o);
7110     },
7111
7112     /**
7113      * Checks to see if this object has any listeners for a specified event
7114      * @param {String} eventName The name of the event to check for
7115      * @return {Boolean} True if the event is being listened for, else false
7116      */
7117     hasListener : function(eventName){
7118         var e = this.events[eventName];
7119         return typeof e == "object" && e.listeners.length > 0;
7120     }
7121 };
7122 /**
7123  * Appends an event handler to this element (shorthand for addListener)
7124  * @param {String}   eventName     The type of event to listen for
7125  * @param {Function} handler        The method the event invokes
7126  * @param {Object}   scope (optional) The scope in which to execute the handler
7127  * function. The handler function's "this" context.
7128  * @param {Object}   options  (optional)
7129  * @method
7130  */
7131 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7132 /**
7133  * Removes a listener (shorthand for removeListener)
7134  * @param {String}   eventName     The type of event to listen for
7135  * @param {Function} handler        The handler to remove
7136  * @param {Object}   scope  (optional) The scope (this object) for the handler
7137  * @method
7138  */
7139 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7140
7141 /**
7142  * Starts capture on the specified Observable. All events will be passed
7143  * to the supplied function with the event name + standard signature of the event
7144  * <b>before</b> the event is fired. If the supplied function returns false,
7145  * the event will not fire.
7146  * @param {Observable} o The Observable to capture
7147  * @param {Function} fn The function to call
7148  * @param {Object} scope (optional) The scope (this object) for the fn
7149  * @static
7150  */
7151 Roo.util.Observable.capture = function(o, fn, scope){
7152     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7153 };
7154
7155 /**
7156  * Removes <b>all</b> added captures from the Observable.
7157  * @param {Observable} o The Observable to release
7158  * @static
7159  */
7160 Roo.util.Observable.releaseCapture = function(o){
7161     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7162 };
7163
7164 (function(){
7165
7166     var createBuffered = function(h, o, scope){
7167         var task = new Roo.util.DelayedTask();
7168         return function(){
7169             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7170         };
7171     };
7172
7173     var createSingle = function(h, e, fn, scope){
7174         return function(){
7175             e.removeListener(fn, scope);
7176             return h.apply(scope, arguments);
7177         };
7178     };
7179
7180     var createDelayed = function(h, o, scope){
7181         return function(){
7182             var args = Array.prototype.slice.call(arguments, 0);
7183             setTimeout(function(){
7184                 h.apply(scope, args);
7185             }, o.delay || 10);
7186         };
7187     };
7188
7189     Roo.util.Event = function(obj, name){
7190         this.name = name;
7191         this.obj = obj;
7192         this.listeners = [];
7193     };
7194
7195     Roo.util.Event.prototype = {
7196         addListener : function(fn, scope, options){
7197             var o = options || {};
7198             scope = scope || this.obj;
7199             if(!this.isListening(fn, scope)){
7200                 var l = {fn: fn, scope: scope, options: o};
7201                 var h = fn;
7202                 if(o.delay){
7203                     h = createDelayed(h, o, scope);
7204                 }
7205                 if(o.single){
7206                     h = createSingle(h, this, fn, scope);
7207                 }
7208                 if(o.buffer){
7209                     h = createBuffered(h, o, scope);
7210                 }
7211                 l.fireFn = h;
7212                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7213                     this.listeners.push(l);
7214                 }else{
7215                     this.listeners = this.listeners.slice(0);
7216                     this.listeners.push(l);
7217                 }
7218             }
7219         },
7220
7221         findListener : function(fn, scope){
7222             scope = scope || this.obj;
7223             var ls = this.listeners;
7224             for(var i = 0, len = ls.length; i < len; i++){
7225                 var l = ls[i];
7226                 if(l.fn == fn && l.scope == scope){
7227                     return i;
7228                 }
7229             }
7230             return -1;
7231         },
7232
7233         isListening : function(fn, scope){
7234             return this.findListener(fn, scope) != -1;
7235         },
7236
7237         removeListener : function(fn, scope){
7238             var index;
7239             if((index = this.findListener(fn, scope)) != -1){
7240                 if(!this.firing){
7241                     this.listeners.splice(index, 1);
7242                 }else{
7243                     this.listeners = this.listeners.slice(0);
7244                     this.listeners.splice(index, 1);
7245                 }
7246                 return true;
7247             }
7248             return false;
7249         },
7250
7251         clearListeners : function(){
7252             this.listeners = [];
7253         },
7254
7255         fire : function(){
7256             var ls = this.listeners, scope, len = ls.length;
7257             if(len > 0){
7258                 this.firing = true;
7259                 var args = Array.prototype.slice.call(arguments, 0);                
7260                 for(var i = 0; i < len; i++){
7261                     var l = ls[i];
7262                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7263                         this.firing = false;
7264                         return false;
7265                     }
7266                 }
7267                 this.firing = false;
7268             }
7269             return true;
7270         }
7271     };
7272 })();/*
7273  * RooJS Library 
7274  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7275  *
7276  * Licence LGPL 
7277  *
7278  */
7279  
7280 /**
7281  * @class Roo.Document
7282  * @extends Roo.util.Observable
7283  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7284  * 
7285  * @param {Object} config the methods and properties of the 'base' class for the application.
7286  * 
7287  *  Generic Page handler - implement this to start your app..
7288  * 
7289  * eg.
7290  *  MyProject = new Roo.Document({
7291         events : {
7292             'load' : true // your events..
7293         },
7294         listeners : {
7295             'ready' : function() {
7296                 // fired on Roo.onReady()
7297             }
7298         }
7299  * 
7300  */
7301 Roo.Document = function(cfg) {
7302      
7303     this.addEvents({ 
7304         'ready' : true
7305     });
7306     Roo.util.Observable.call(this,cfg);
7307     
7308     var _this = this;
7309     
7310     Roo.onReady(function() {
7311         _this.fireEvent('ready');
7312     },null,false);
7313     
7314     
7315 }
7316
7317 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7318  * Based on:
7319  * Ext JS Library 1.1.1
7320  * Copyright(c) 2006-2007, Ext JS, LLC.
7321  *
7322  * Originally Released Under LGPL - original licence link has changed is not relivant.
7323  *
7324  * Fork - LGPL
7325  * <script type="text/javascript">
7326  */
7327
7328 /**
7329  * @class Roo.EventManager
7330  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7331  * several useful events directly.
7332  * See {@link Roo.EventObject} for more details on normalized event objects.
7333  * @static
7334  */
7335 Roo.EventManager = function(){
7336     var docReadyEvent, docReadyProcId, docReadyState = false;
7337     var resizeEvent, resizeTask, textEvent, textSize;
7338     var E = Roo.lib.Event;
7339     var D = Roo.lib.Dom;
7340
7341     
7342     
7343
7344     var fireDocReady = function(){
7345         if(!docReadyState){
7346             docReadyState = true;
7347             Roo.isReady = true;
7348             if(docReadyProcId){
7349                 clearInterval(docReadyProcId);
7350             }
7351             if(Roo.isGecko || Roo.isOpera) {
7352                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7353             }
7354             if(Roo.isIE){
7355                 var defer = document.getElementById("ie-deferred-loader");
7356                 if(defer){
7357                     defer.onreadystatechange = null;
7358                     defer.parentNode.removeChild(defer);
7359                 }
7360             }
7361             if(docReadyEvent){
7362                 docReadyEvent.fire();
7363                 docReadyEvent.clearListeners();
7364             }
7365         }
7366     };
7367     
7368     var initDocReady = function(){
7369         docReadyEvent = new Roo.util.Event();
7370         if(Roo.isGecko || Roo.isOpera) {
7371             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7372         }else if(Roo.isIE){
7373             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7374             var defer = document.getElementById("ie-deferred-loader");
7375             defer.onreadystatechange = function(){
7376                 if(this.readyState == "complete"){
7377                     fireDocReady();
7378                 }
7379             };
7380         }else if(Roo.isSafari){ 
7381             docReadyProcId = setInterval(function(){
7382                 var rs = document.readyState;
7383                 if(rs == "complete") {
7384                     fireDocReady();     
7385                  }
7386             }, 10);
7387         }
7388         // no matter what, make sure it fires on load
7389         E.on(window, "load", fireDocReady);
7390     };
7391
7392     var createBuffered = function(h, o){
7393         var task = new Roo.util.DelayedTask(h);
7394         return function(e){
7395             // create new event object impl so new events don't wipe out properties
7396             e = new Roo.EventObjectImpl(e);
7397             task.delay(o.buffer, h, null, [e]);
7398         };
7399     };
7400
7401     var createSingle = function(h, el, ename, fn){
7402         return function(e){
7403             Roo.EventManager.removeListener(el, ename, fn);
7404             h(e);
7405         };
7406     };
7407
7408     var createDelayed = function(h, o){
7409         return function(e){
7410             // create new event object impl so new events don't wipe out properties
7411             e = new Roo.EventObjectImpl(e);
7412             setTimeout(function(){
7413                 h(e);
7414             }, o.delay || 10);
7415         };
7416     };
7417     var transitionEndVal = false;
7418     
7419     var transitionEnd = function()
7420     {
7421         if (transitionEndVal) {
7422             return transitionEndVal;
7423         }
7424         var el = document.createElement('div');
7425
7426         var transEndEventNames = {
7427             WebkitTransition : 'webkitTransitionEnd',
7428             MozTransition    : 'transitionend',
7429             OTransition      : 'oTransitionEnd otransitionend',
7430             transition       : 'transitionend'
7431         };
7432     
7433         for (var name in transEndEventNames) {
7434             if (el.style[name] !== undefined) {
7435                 transitionEndVal = transEndEventNames[name];
7436                 return  transitionEndVal ;
7437             }
7438         }
7439     }
7440     
7441   
7442
7443     var listen = function(element, ename, opt, fn, scope)
7444     {
7445         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7446         fn = fn || o.fn; scope = scope || o.scope;
7447         var el = Roo.getDom(element);
7448         
7449         
7450         if(!el){
7451             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7452         }
7453         
7454         if (ename == 'transitionend') {
7455             ename = transitionEnd();
7456         }
7457         var h = function(e){
7458             e = Roo.EventObject.setEvent(e);
7459             var t;
7460             if(o.delegate){
7461                 t = e.getTarget(o.delegate, el);
7462                 if(!t){
7463                     return;
7464                 }
7465             }else{
7466                 t = e.target;
7467             }
7468             if(o.stopEvent === true){
7469                 e.stopEvent();
7470             }
7471             if(o.preventDefault === true){
7472                e.preventDefault();
7473             }
7474             if(o.stopPropagation === true){
7475                 e.stopPropagation();
7476             }
7477
7478             if(o.normalized === false){
7479                 e = e.browserEvent;
7480             }
7481
7482             fn.call(scope || el, e, t, o);
7483         };
7484         if(o.delay){
7485             h = createDelayed(h, o);
7486         }
7487         if(o.single){
7488             h = createSingle(h, el, ename, fn);
7489         }
7490         if(o.buffer){
7491             h = createBuffered(h, o);
7492         }
7493         
7494         fn._handlers = fn._handlers || [];
7495         
7496         
7497         fn._handlers.push([Roo.id(el), ename, h]);
7498         
7499         
7500          
7501         E.on(el, ename, h); // this adds the actuall listener to the object..
7502         
7503         
7504         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7505             el.addEventListener("DOMMouseScroll", h, false);
7506             E.on(window, 'unload', function(){
7507                 el.removeEventListener("DOMMouseScroll", h, false);
7508             });
7509         }
7510         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7511             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7512         }
7513         return h;
7514     };
7515
7516     var stopListening = function(el, ename, fn){
7517         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7518         if(hds){
7519             for(var i = 0, len = hds.length; i < len; i++){
7520                 var h = hds[i];
7521                 if(h[0] == id && h[1] == ename){
7522                     hd = h[2];
7523                     hds.splice(i, 1);
7524                     break;
7525                 }
7526             }
7527         }
7528         E.un(el, ename, hd);
7529         el = Roo.getDom(el);
7530         if(ename == "mousewheel" && el.addEventListener){
7531             el.removeEventListener("DOMMouseScroll", hd, false);
7532         }
7533         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7534             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7535         }
7536     };
7537
7538     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7539     
7540     var pub = {
7541         
7542         
7543         /** 
7544          * Fix for doc tools
7545          * @scope Roo.EventManager
7546          */
7547         
7548         
7549         /** 
7550          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7551          * object with a Roo.EventObject
7552          * @param {Function} fn        The method the event invokes
7553          * @param {Object}   scope    An object that becomes the scope of the handler
7554          * @param {boolean}  override If true, the obj passed in becomes
7555          *                             the execution scope of the listener
7556          * @return {Function} The wrapped function
7557          * @deprecated
7558          */
7559         wrap : function(fn, scope, override){
7560             return function(e){
7561                 Roo.EventObject.setEvent(e);
7562                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7563             };
7564         },
7565         
7566         /**
7567      * Appends an event handler to an element (shorthand for addListener)
7568      * @param {String/HTMLElement}   element        The html element or id to assign the
7569      * @param {String}   eventName The type of event to listen for
7570      * @param {Function} handler The method the event invokes
7571      * @param {Object}   scope (optional) The scope in which to execute the handler
7572      * function. The handler function's "this" context.
7573      * @param {Object}   options (optional) An object containing handler configuration
7574      * properties. This may contain any of the following properties:<ul>
7575      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7576      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7577      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7578      * <li>preventDefault {Boolean} True to prevent the default action</li>
7579      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7580      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7581      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7582      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7583      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7584      * by the specified number of milliseconds. If the event fires again within that time, the original
7585      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7586      * </ul><br>
7587      * <p>
7588      * <b>Combining Options</b><br>
7589      * Using the options argument, it is possible to combine different types of listeners:<br>
7590      * <br>
7591      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7592      * Code:<pre><code>
7593 el.on('click', this.onClick, this, {
7594     single: true,
7595     delay: 100,
7596     stopEvent : true,
7597     forumId: 4
7598 });</code></pre>
7599      * <p>
7600      * <b>Attaching multiple handlers in 1 call</b><br>
7601       * The method also allows for a single argument to be passed which is a config object containing properties
7602      * which specify multiple handlers.
7603      * <p>
7604      * Code:<pre><code>
7605 el.on({
7606     'click' : {
7607         fn: this.onClick
7608         scope: this,
7609         delay: 100
7610     },
7611     'mouseover' : {
7612         fn: this.onMouseOver
7613         scope: this
7614     },
7615     'mouseout' : {
7616         fn: this.onMouseOut
7617         scope: this
7618     }
7619 });</code></pre>
7620      * <p>
7621      * Or a shorthand syntax:<br>
7622      * Code:<pre><code>
7623 el.on({
7624     'click' : this.onClick,
7625     'mouseover' : this.onMouseOver,
7626     'mouseout' : this.onMouseOut
7627     scope: this
7628 });</code></pre>
7629      */
7630         addListener : function(element, eventName, fn, scope, options){
7631             if(typeof eventName == "object"){
7632                 var o = eventName;
7633                 for(var e in o){
7634                     if(propRe.test(e)){
7635                         continue;
7636                     }
7637                     if(typeof o[e] == "function"){
7638                         // shared options
7639                         listen(element, e, o, o[e], o.scope);
7640                     }else{
7641                         // individual options
7642                         listen(element, e, o[e]);
7643                     }
7644                 }
7645                 return;
7646             }
7647             return listen(element, eventName, options, fn, scope);
7648         },
7649         
7650         /**
7651          * Removes an event handler
7652          *
7653          * @param {String/HTMLElement}   element        The id or html element to remove the 
7654          *                             event from
7655          * @param {String}   eventName     The type of event
7656          * @param {Function} fn
7657          * @return {Boolean} True if a listener was actually removed
7658          */
7659         removeListener : function(element, eventName, fn){
7660             return stopListening(element, eventName, fn);
7661         },
7662         
7663         /**
7664          * Fires when the document is ready (before onload and before images are loaded). Can be 
7665          * accessed shorthanded Roo.onReady().
7666          * @param {Function} fn        The method the event invokes
7667          * @param {Object}   scope    An  object that becomes the scope of the handler
7668          * @param {boolean}  options
7669          */
7670         onDocumentReady : function(fn, scope, options){
7671             if(docReadyState){ // if it already fired
7672                 docReadyEvent.addListener(fn, scope, options);
7673                 docReadyEvent.fire();
7674                 docReadyEvent.clearListeners();
7675                 return;
7676             }
7677             if(!docReadyEvent){
7678                 initDocReady();
7679             }
7680             docReadyEvent.addListener(fn, scope, options);
7681         },
7682         
7683         /**
7684          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7685          * @param {Function} fn        The method the event invokes
7686          * @param {Object}   scope    An object that becomes the scope of the handler
7687          * @param {boolean}  options
7688          */
7689         onWindowResize : function(fn, scope, options)
7690         {
7691             if(!resizeEvent){
7692                 resizeEvent = new Roo.util.Event();
7693                 resizeTask = new Roo.util.DelayedTask(function(){
7694                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7695                 });
7696                 E.on(window, "resize", function()
7697                 {
7698                     if (Roo.isIE) {
7699                         resizeTask.delay(50);
7700                     } else {
7701                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7702                     }
7703                 });
7704             }
7705             resizeEvent.addListener(fn, scope, options);
7706         },
7707
7708         /**
7709          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7710          * @param {Function} fn        The method the event invokes
7711          * @param {Object}   scope    An object that becomes the scope of the handler
7712          * @param {boolean}  options
7713          */
7714         onTextResize : function(fn, scope, options){
7715             if(!textEvent){
7716                 textEvent = new Roo.util.Event();
7717                 var textEl = new Roo.Element(document.createElement('div'));
7718                 textEl.dom.className = 'x-text-resize';
7719                 textEl.dom.innerHTML = 'X';
7720                 textEl.appendTo(document.body);
7721                 textSize = textEl.dom.offsetHeight;
7722                 setInterval(function(){
7723                     if(textEl.dom.offsetHeight != textSize){
7724                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7725                     }
7726                 }, this.textResizeInterval);
7727             }
7728             textEvent.addListener(fn, scope, options);
7729         },
7730
7731         /**
7732          * Removes the passed window resize listener.
7733          * @param {Function} fn        The method the event invokes
7734          * @param {Object}   scope    The scope of handler
7735          */
7736         removeResizeListener : function(fn, scope){
7737             if(resizeEvent){
7738                 resizeEvent.removeListener(fn, scope);
7739             }
7740         },
7741
7742         // private
7743         fireResize : function(){
7744             if(resizeEvent){
7745                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7746             }   
7747         },
7748         /**
7749          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7750          */
7751         ieDeferSrc : false,
7752         /**
7753          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7754          */
7755         textResizeInterval : 50
7756     };
7757     
7758     /**
7759      * Fix for doc tools
7760      * @scopeAlias pub=Roo.EventManager
7761      */
7762     
7763      /**
7764      * Appends an event handler to an element (shorthand for addListener)
7765      * @param {String/HTMLElement}   element        The html element or id to assign the
7766      * @param {String}   eventName The type of event to listen for
7767      * @param {Function} handler The method the event invokes
7768      * @param {Object}   scope (optional) The scope in which to execute the handler
7769      * function. The handler function's "this" context.
7770      * @param {Object}   options (optional) An object containing handler configuration
7771      * properties. This may contain any of the following properties:<ul>
7772      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7773      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7774      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7775      * <li>preventDefault {Boolean} True to prevent the default action</li>
7776      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7777      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7778      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7779      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7780      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7781      * by the specified number of milliseconds. If the event fires again within that time, the original
7782      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7783      * </ul><br>
7784      * <p>
7785      * <b>Combining Options</b><br>
7786      * Using the options argument, it is possible to combine different types of listeners:<br>
7787      * <br>
7788      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7789      * Code:<pre><code>
7790 el.on('click', this.onClick, this, {
7791     single: true,
7792     delay: 100,
7793     stopEvent : true,
7794     forumId: 4
7795 });</code></pre>
7796      * <p>
7797      * <b>Attaching multiple handlers in 1 call</b><br>
7798       * The method also allows for a single argument to be passed which is a config object containing properties
7799      * which specify multiple handlers.
7800      * <p>
7801      * Code:<pre><code>
7802 el.on({
7803     'click' : {
7804         fn: this.onClick
7805         scope: this,
7806         delay: 100
7807     },
7808     'mouseover' : {
7809         fn: this.onMouseOver
7810         scope: this
7811     },
7812     'mouseout' : {
7813         fn: this.onMouseOut
7814         scope: this
7815     }
7816 });</code></pre>
7817      * <p>
7818      * Or a shorthand syntax:<br>
7819      * Code:<pre><code>
7820 el.on({
7821     'click' : this.onClick,
7822     'mouseover' : this.onMouseOver,
7823     'mouseout' : this.onMouseOut
7824     scope: this
7825 });</code></pre>
7826      */
7827     pub.on = pub.addListener;
7828     pub.un = pub.removeListener;
7829
7830     pub.stoppedMouseDownEvent = new Roo.util.Event();
7831     return pub;
7832 }();
7833 /**
7834   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7835   * @param {Function} fn        The method the event invokes
7836   * @param {Object}   scope    An  object that becomes the scope of the handler
7837   * @param {boolean}  override If true, the obj passed in becomes
7838   *                             the execution scope of the listener
7839   * @member Roo
7840   * @method onReady
7841  */
7842 Roo.onReady = Roo.EventManager.onDocumentReady;
7843
7844 Roo.onReady(function(){
7845     var bd = Roo.get(document.body);
7846     if(!bd){ return; }
7847
7848     var cls = [
7849             Roo.isIE ? "roo-ie"
7850             : Roo.isIE11 ? "roo-ie11"
7851             : Roo.isEdge ? "roo-edge"
7852             : Roo.isGecko ? "roo-gecko"
7853             : Roo.isOpera ? "roo-opera"
7854             : Roo.isSafari ? "roo-safari" : ""];
7855
7856     if(Roo.isMac){
7857         cls.push("roo-mac");
7858     }
7859     if(Roo.isLinux){
7860         cls.push("roo-linux");
7861     }
7862     if(Roo.isIOS){
7863         cls.push("roo-ios");
7864     }
7865     if(Roo.isTouch){
7866         cls.push("roo-touch");
7867     }
7868     if(Roo.isBorderBox){
7869         cls.push('roo-border-box');
7870     }
7871     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7872         var p = bd.dom.parentNode;
7873         if(p){
7874             p.className += ' roo-strict';
7875         }
7876     }
7877     bd.addClass(cls.join(' '));
7878 });
7879
7880 /**
7881  * @class Roo.EventObject
7882  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7883  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7884  * Example:
7885  * <pre><code>
7886  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7887     e.preventDefault();
7888     var target = e.getTarget();
7889     ...
7890  }
7891  var myDiv = Roo.get("myDiv");
7892  myDiv.on("click", handleClick);
7893  //or
7894  Roo.EventManager.on("myDiv", 'click', handleClick);
7895  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7896  </code></pre>
7897  * @static
7898  */
7899 Roo.EventObject = function(){
7900     
7901     var E = Roo.lib.Event;
7902     
7903     // safari keypress events for special keys return bad keycodes
7904     var safariKeys = {
7905         63234 : 37, // left
7906         63235 : 39, // right
7907         63232 : 38, // up
7908         63233 : 40, // down
7909         63276 : 33, // page up
7910         63277 : 34, // page down
7911         63272 : 46, // delete
7912         63273 : 36, // home
7913         63275 : 35  // end
7914     };
7915
7916     // normalize button clicks
7917     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7918                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7919
7920     Roo.EventObjectImpl = function(e){
7921         if(e){
7922             this.setEvent(e.browserEvent || e);
7923         }
7924     };
7925     Roo.EventObjectImpl.prototype = {
7926         /**
7927          * Used to fix doc tools.
7928          * @scope Roo.EventObject.prototype
7929          */
7930             
7931
7932         
7933         
7934         /** The normal browser event */
7935         browserEvent : null,
7936         /** The button pressed in a mouse event */
7937         button : -1,
7938         /** True if the shift key was down during the event */
7939         shiftKey : false,
7940         /** True if the control key was down during the event */
7941         ctrlKey : false,
7942         /** True if the alt key was down during the event */
7943         altKey : false,
7944
7945         /** Key constant 
7946         * @type Number */
7947         BACKSPACE : 8,
7948         /** Key constant 
7949         * @type Number */
7950         TAB : 9,
7951         /** Key constant 
7952         * @type Number */
7953         RETURN : 13,
7954         /** Key constant 
7955         * @type Number */
7956         ENTER : 13,
7957         /** Key constant 
7958         * @type Number */
7959         SHIFT : 16,
7960         /** Key constant 
7961         * @type Number */
7962         CONTROL : 17,
7963         /** Key constant 
7964         * @type Number */
7965         ESC : 27,
7966         /** Key constant 
7967         * @type Number */
7968         SPACE : 32,
7969         /** Key constant 
7970         * @type Number */
7971         PAGEUP : 33,
7972         /** Key constant 
7973         * @type Number */
7974         PAGEDOWN : 34,
7975         /** Key constant 
7976         * @type Number */
7977         END : 35,
7978         /** Key constant 
7979         * @type Number */
7980         HOME : 36,
7981         /** Key constant 
7982         * @type Number */
7983         LEFT : 37,
7984         /** Key constant 
7985         * @type Number */
7986         UP : 38,
7987         /** Key constant 
7988         * @type Number */
7989         RIGHT : 39,
7990         /** Key constant 
7991         * @type Number */
7992         DOWN : 40,
7993         /** Key constant 
7994         * @type Number */
7995         DELETE : 46,
7996         /** Key constant 
7997         * @type Number */
7998         F5 : 116,
7999
8000            /** @private */
8001         setEvent : function(e){
8002             if(e == this || (e && e.browserEvent)){ // already wrapped
8003                 return e;
8004             }
8005             this.browserEvent = e;
8006             if(e){
8007                 // normalize buttons
8008                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8009                 if(e.type == 'click' && this.button == -1){
8010                     this.button = 0;
8011                 }
8012                 this.type = e.type;
8013                 this.shiftKey = e.shiftKey;
8014                 // mac metaKey behaves like ctrlKey
8015                 this.ctrlKey = e.ctrlKey || e.metaKey;
8016                 this.altKey = e.altKey;
8017                 // in getKey these will be normalized for the mac
8018                 this.keyCode = e.keyCode;
8019                 // keyup warnings on firefox.
8020                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8021                 // cache the target for the delayed and or buffered events
8022                 this.target = E.getTarget(e);
8023                 // same for XY
8024                 this.xy = E.getXY(e);
8025             }else{
8026                 this.button = -1;
8027                 this.shiftKey = false;
8028                 this.ctrlKey = false;
8029                 this.altKey = false;
8030                 this.keyCode = 0;
8031                 this.charCode =0;
8032                 this.target = null;
8033                 this.xy = [0, 0];
8034             }
8035             return this;
8036         },
8037
8038         /**
8039          * Stop the event (preventDefault and stopPropagation)
8040          */
8041         stopEvent : function(){
8042             if(this.browserEvent){
8043                 if(this.browserEvent.type == 'mousedown'){
8044                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8045                 }
8046                 E.stopEvent(this.browserEvent);
8047             }
8048         },
8049
8050         /**
8051          * Prevents the browsers default handling of the event.
8052          */
8053         preventDefault : function(){
8054             if(this.browserEvent){
8055                 E.preventDefault(this.browserEvent);
8056             }
8057         },
8058
8059         /** @private */
8060         isNavKeyPress : function(){
8061             var k = this.keyCode;
8062             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8063             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8064         },
8065
8066         isSpecialKey : function(){
8067             var k = this.keyCode;
8068             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8069             (k == 16) || (k == 17) ||
8070             (k >= 18 && k <= 20) ||
8071             (k >= 33 && k <= 35) ||
8072             (k >= 36 && k <= 39) ||
8073             (k >= 44 && k <= 45);
8074         },
8075         /**
8076          * Cancels bubbling of the event.
8077          */
8078         stopPropagation : function(){
8079             if(this.browserEvent){
8080                 if(this.type == 'mousedown'){
8081                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8082                 }
8083                 E.stopPropagation(this.browserEvent);
8084             }
8085         },
8086
8087         /**
8088          * Gets the key code for the event.
8089          * @return {Number}
8090          */
8091         getCharCode : function(){
8092             return this.charCode || this.keyCode;
8093         },
8094
8095         /**
8096          * Returns a normalized keyCode for the event.
8097          * @return {Number} The key code
8098          */
8099         getKey : function(){
8100             var k = this.keyCode || this.charCode;
8101             return Roo.isSafari ? (safariKeys[k] || k) : k;
8102         },
8103
8104         /**
8105          * Gets the x coordinate of the event.
8106          * @return {Number}
8107          */
8108         getPageX : function(){
8109             return this.xy[0];
8110         },
8111
8112         /**
8113          * Gets the y coordinate of the event.
8114          * @return {Number}
8115          */
8116         getPageY : function(){
8117             return this.xy[1];
8118         },
8119
8120         /**
8121          * Gets the time of the event.
8122          * @return {Number}
8123          */
8124         getTime : function(){
8125             if(this.browserEvent){
8126                 return E.getTime(this.browserEvent);
8127             }
8128             return null;
8129         },
8130
8131         /**
8132          * Gets the page coordinates of the event.
8133          * @return {Array} The xy values like [x, y]
8134          */
8135         getXY : function(){
8136             return this.xy;
8137         },
8138
8139         /**
8140          * Gets the target for the event.
8141          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8142          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8143                 search as a number or element (defaults to 10 || document.body)
8144          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8145          * @return {HTMLelement}
8146          */
8147         getTarget : function(selector, maxDepth, returnEl){
8148             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8149         },
8150         /**
8151          * Gets the related target.
8152          * @return {HTMLElement}
8153          */
8154         getRelatedTarget : function(){
8155             if(this.browserEvent){
8156                 return E.getRelatedTarget(this.browserEvent);
8157             }
8158             return null;
8159         },
8160
8161         /**
8162          * Normalizes mouse wheel delta across browsers
8163          * @return {Number} The delta
8164          */
8165         getWheelDelta : function(){
8166             var e = this.browserEvent;
8167             var delta = 0;
8168             if(e.wheelDelta){ /* IE/Opera. */
8169                 delta = e.wheelDelta/120;
8170             }else if(e.detail){ /* Mozilla case. */
8171                 delta = -e.detail/3;
8172             }
8173             return delta;
8174         },
8175
8176         /**
8177          * Returns true if the control, meta, shift or alt key was pressed during this event.
8178          * @return {Boolean}
8179          */
8180         hasModifier : function(){
8181             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8182         },
8183
8184         /**
8185          * Returns true if the target of this event equals el or is a child of el
8186          * @param {String/HTMLElement/Element} el
8187          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8188          * @return {Boolean}
8189          */
8190         within : function(el, related){
8191             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8192             return t && Roo.fly(el).contains(t);
8193         },
8194
8195         getPoint : function(){
8196             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8197         }
8198     };
8199
8200     return new Roo.EventObjectImpl();
8201 }();
8202             
8203     /*
8204  * Based on:
8205  * Ext JS Library 1.1.1
8206  * Copyright(c) 2006-2007, Ext JS, LLC.
8207  *
8208  * Originally Released Under LGPL - original licence link has changed is not relivant.
8209  *
8210  * Fork - LGPL
8211  * <script type="text/javascript">
8212  */
8213
8214  
8215 // was in Composite Element!??!?!
8216  
8217 (function(){
8218     var D = Roo.lib.Dom;
8219     var E = Roo.lib.Event;
8220     var A = Roo.lib.Anim;
8221
8222     // local style camelizing for speed
8223     var propCache = {};
8224     var camelRe = /(-[a-z])/gi;
8225     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8226     var view = document.defaultView;
8227
8228 /**
8229  * @class Roo.Element
8230  * Represents an Element in the DOM.<br><br>
8231  * Usage:<br>
8232 <pre><code>
8233 var el = Roo.get("my-div");
8234
8235 // or with getEl
8236 var el = getEl("my-div");
8237
8238 // or with a DOM element
8239 var el = Roo.get(myDivElement);
8240 </code></pre>
8241  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8242  * each call instead of constructing a new one.<br><br>
8243  * <b>Animations</b><br />
8244  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8245  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8246 <pre>
8247 Option    Default   Description
8248 --------- --------  ---------------------------------------------
8249 duration  .35       The duration of the animation in seconds
8250 easing    easeOut   The YUI easing method
8251 callback  none      A function to execute when the anim completes
8252 scope     this      The scope (this) of the callback function
8253 </pre>
8254 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8255 * manipulate the animation. Here's an example:
8256 <pre><code>
8257 var el = Roo.get("my-div");
8258
8259 // no animation
8260 el.setWidth(100);
8261
8262 // default animation
8263 el.setWidth(100, true);
8264
8265 // animation with some options set
8266 el.setWidth(100, {
8267     duration: 1,
8268     callback: this.foo,
8269     scope: this
8270 });
8271
8272 // using the "anim" property to get the Anim object
8273 var opt = {
8274     duration: 1,
8275     callback: this.foo,
8276     scope: this
8277 };
8278 el.setWidth(100, opt);
8279 ...
8280 if(opt.anim.isAnimated()){
8281     opt.anim.stop();
8282 }
8283 </code></pre>
8284 * <b> Composite (Collections of) Elements</b><br />
8285  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8286  * @constructor Create a new Element directly.
8287  * @param {String/HTMLElement} element
8288  * @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).
8289  */
8290     Roo.Element = function(element, forceNew)
8291     {
8292         var dom = typeof element == "string" ?
8293                 document.getElementById(element) : element;
8294         
8295         this.listeners = {};
8296         
8297         if(!dom){ // invalid id/element
8298             return null;
8299         }
8300         var id = dom.id;
8301         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8302             return Roo.Element.cache[id];
8303         }
8304
8305         /**
8306          * The DOM element
8307          * @type HTMLElement
8308          */
8309         this.dom = dom;
8310
8311         /**
8312          * The DOM element ID
8313          * @type String
8314          */
8315         this.id = id || Roo.id(dom);
8316         
8317         return this; // assumed for cctor?
8318     };
8319
8320     var El = Roo.Element;
8321
8322     El.prototype = {
8323         /**
8324          * The element's default display mode  (defaults to "") 
8325          * @type String
8326          */
8327         originalDisplay : "",
8328
8329         
8330         // note this is overridden in BS version..
8331         visibilityMode : 1, 
8332         /**
8333          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8334          * @type String
8335          */
8336         defaultUnit : "px",
8337         
8338         /**
8339          * Sets the element's visibility mode. When setVisible() is called it
8340          * will use this to determine whether to set the visibility or the display property.
8341          * @param visMode Element.VISIBILITY or Element.DISPLAY
8342          * @return {Roo.Element} this
8343          */
8344         setVisibilityMode : function(visMode){
8345             this.visibilityMode = visMode;
8346             return this;
8347         },
8348         /**
8349          * Convenience method for setVisibilityMode(Element.DISPLAY)
8350          * @param {String} display (optional) What to set display to when visible
8351          * @return {Roo.Element} this
8352          */
8353         enableDisplayMode : function(display){
8354             this.setVisibilityMode(El.DISPLAY);
8355             if(typeof display != "undefined") { this.originalDisplay = display; }
8356             return this;
8357         },
8358
8359         /**
8360          * 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)
8361          * @param {String} selector The simple selector to test
8362          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8363                 search as a number or element (defaults to 10 || document.body)
8364          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8365          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8366          */
8367         findParent : function(simpleSelector, maxDepth, returnEl){
8368             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8369             maxDepth = maxDepth || 50;
8370             if(typeof maxDepth != "number"){
8371                 stopEl = Roo.getDom(maxDepth);
8372                 maxDepth = 10;
8373             }
8374             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8375                 if(dq.is(p, simpleSelector)){
8376                     return returnEl ? Roo.get(p) : p;
8377                 }
8378                 depth++;
8379                 p = p.parentNode;
8380             }
8381             return null;
8382         },
8383
8384
8385         /**
8386          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8387          * @param {String} selector The simple selector to test
8388          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8389                 search as a number or element (defaults to 10 || document.body)
8390          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8391          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8392          */
8393         findParentNode : function(simpleSelector, maxDepth, returnEl){
8394             var p = Roo.fly(this.dom.parentNode, '_internal');
8395             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8396         },
8397         
8398         /**
8399          * Looks at  the scrollable parent element
8400          */
8401         findScrollableParent : function()
8402         {
8403             var overflowRegex = /(auto|scroll)/;
8404             
8405             if(this.getStyle('position') === 'fixed'){
8406                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8407             }
8408             
8409             var excludeStaticParent = this.getStyle('position') === "absolute";
8410             
8411             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8412                 
8413                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8414                     continue;
8415                 }
8416                 
8417                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8418                     return parent;
8419                 }
8420                 
8421                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8422                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8423                 }
8424             }
8425             
8426             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8427         },
8428
8429         /**
8430          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8431          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8432          * @param {String} selector The simple selector to test
8433          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8434                 search as a number or element (defaults to 10 || document.body)
8435          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8436          */
8437         up : function(simpleSelector, maxDepth){
8438             return this.findParentNode(simpleSelector, maxDepth, true);
8439         },
8440
8441
8442
8443         /**
8444          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8445          * @param {String} selector The simple selector to test
8446          * @return {Boolean} True if this element matches the selector, else false
8447          */
8448         is : function(simpleSelector){
8449             return Roo.DomQuery.is(this.dom, simpleSelector);
8450         },
8451
8452         /**
8453          * Perform animation on this element.
8454          * @param {Object} args The YUI animation control args
8455          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8456          * @param {Function} onComplete (optional) Function to call when animation completes
8457          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8458          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8459          * @return {Roo.Element} this
8460          */
8461         animate : function(args, duration, onComplete, easing, animType){
8462             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8463             return this;
8464         },
8465
8466         /*
8467          * @private Internal animation call
8468          */
8469         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8470             animType = animType || 'run';
8471             opt = opt || {};
8472             var anim = Roo.lib.Anim[animType](
8473                 this.dom, args,
8474                 (opt.duration || defaultDur) || .35,
8475                 (opt.easing || defaultEase) || 'easeOut',
8476                 function(){
8477                     Roo.callback(cb, this);
8478                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8479                 },
8480                 this
8481             );
8482             opt.anim = anim;
8483             return anim;
8484         },
8485
8486         // private legacy anim prep
8487         preanim : function(a, i){
8488             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8489         },
8490
8491         /**
8492          * Removes worthless text nodes
8493          * @param {Boolean} forceReclean (optional) By default the element
8494          * keeps track if it has been cleaned already so
8495          * you can call this over and over. However, if you update the element and
8496          * need to force a reclean, you can pass true.
8497          */
8498         clean : function(forceReclean){
8499             if(this.isCleaned && forceReclean !== true){
8500                 return this;
8501             }
8502             var ns = /\S/;
8503             var d = this.dom, n = d.firstChild, ni = -1;
8504             while(n){
8505                 var nx = n.nextSibling;
8506                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8507                     d.removeChild(n);
8508                 }else{
8509                     n.nodeIndex = ++ni;
8510                 }
8511                 n = nx;
8512             }
8513             this.isCleaned = true;
8514             return this;
8515         },
8516
8517         // private
8518         calcOffsetsTo : function(el){
8519             el = Roo.get(el);
8520             var d = el.dom;
8521             var restorePos = false;
8522             if(el.getStyle('position') == 'static'){
8523                 el.position('relative');
8524                 restorePos = true;
8525             }
8526             var x = 0, y =0;
8527             var op = this.dom;
8528             while(op && op != d && op.tagName != 'HTML'){
8529                 x+= op.offsetLeft;
8530                 y+= op.offsetTop;
8531                 op = op.offsetParent;
8532             }
8533             if(restorePos){
8534                 el.position('static');
8535             }
8536             return [x, y];
8537         },
8538
8539         /**
8540          * Scrolls this element into view within the passed container.
8541          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8542          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8543          * @return {Roo.Element} this
8544          */
8545         scrollIntoView : function(container, hscroll){
8546             var c = Roo.getDom(container) || document.body;
8547             var el = this.dom;
8548
8549             var o = this.calcOffsetsTo(c),
8550                 l = o[0],
8551                 t = o[1],
8552                 b = t+el.offsetHeight,
8553                 r = l+el.offsetWidth;
8554
8555             var ch = c.clientHeight;
8556             var ct = parseInt(c.scrollTop, 10);
8557             var cl = parseInt(c.scrollLeft, 10);
8558             var cb = ct + ch;
8559             var cr = cl + c.clientWidth;
8560
8561             if(t < ct){
8562                 c.scrollTop = t;
8563             }else if(b > cb){
8564                 c.scrollTop = b-ch;
8565             }
8566
8567             if(hscroll !== false){
8568                 if(l < cl){
8569                     c.scrollLeft = l;
8570                 }else if(r > cr){
8571                     c.scrollLeft = r-c.clientWidth;
8572                 }
8573             }
8574             return this;
8575         },
8576
8577         // private
8578         scrollChildIntoView : function(child, hscroll){
8579             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8580         },
8581
8582         /**
8583          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8584          * the new height may not be available immediately.
8585          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8586          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8587          * @param {Function} onComplete (optional) Function to call when animation completes
8588          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8589          * @return {Roo.Element} this
8590          */
8591         autoHeight : function(animate, duration, onComplete, easing){
8592             var oldHeight = this.getHeight();
8593             this.clip();
8594             this.setHeight(1); // force clipping
8595             setTimeout(function(){
8596                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8597                 if(!animate){
8598                     this.setHeight(height);
8599                     this.unclip();
8600                     if(typeof onComplete == "function"){
8601                         onComplete();
8602                     }
8603                 }else{
8604                     this.setHeight(oldHeight); // restore original height
8605                     this.setHeight(height, animate, duration, function(){
8606                         this.unclip();
8607                         if(typeof onComplete == "function") { onComplete(); }
8608                     }.createDelegate(this), easing);
8609                 }
8610             }.createDelegate(this), 0);
8611             return this;
8612         },
8613
8614         /**
8615          * Returns true if this element is an ancestor of the passed element
8616          * @param {HTMLElement/String} el The element to check
8617          * @return {Boolean} True if this element is an ancestor of el, else false
8618          */
8619         contains : function(el){
8620             if(!el){return false;}
8621             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8622         },
8623
8624         /**
8625          * Checks whether the element is currently visible using both visibility and display properties.
8626          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8627          * @return {Boolean} True if the element is currently visible, else false
8628          */
8629         isVisible : function(deep) {
8630             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8631             if(deep !== true || !vis){
8632                 return vis;
8633             }
8634             var p = this.dom.parentNode;
8635             while(p && p.tagName.toLowerCase() != "body"){
8636                 if(!Roo.fly(p, '_isVisible').isVisible()){
8637                     return false;
8638                 }
8639                 p = p.parentNode;
8640             }
8641             return true;
8642         },
8643
8644         /**
8645          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8646          * @param {String} selector The CSS selector
8647          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8648          * @return {CompositeElement/CompositeElementLite} The composite element
8649          */
8650         select : function(selector, unique){
8651             return El.select(selector, unique, this.dom);
8652         },
8653
8654         /**
8655          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8656          * @param {String} selector The CSS selector
8657          * @return {Array} An array of the matched nodes
8658          */
8659         query : function(selector, unique){
8660             return Roo.DomQuery.select(selector, this.dom);
8661         },
8662
8663         /**
8664          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8665          * @param {String} selector The CSS selector
8666          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8667          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8668          */
8669         child : function(selector, returnDom){
8670             var n = Roo.DomQuery.selectNode(selector, this.dom);
8671             return returnDom ? n : Roo.get(n);
8672         },
8673
8674         /**
8675          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8676          * @param {String} selector The CSS selector
8677          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8678          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8679          */
8680         down : function(selector, returnDom){
8681             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8682             return returnDom ? n : Roo.get(n);
8683         },
8684
8685         /**
8686          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8687          * @param {String} group The group the DD object is member of
8688          * @param {Object} config The DD config object
8689          * @param {Object} overrides An object containing methods to override/implement on the DD object
8690          * @return {Roo.dd.DD} The DD object
8691          */
8692         initDD : function(group, config, overrides){
8693             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8694             return Roo.apply(dd, overrides);
8695         },
8696
8697         /**
8698          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8699          * @param {String} group The group the DDProxy object is member of
8700          * @param {Object} config The DDProxy config object
8701          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8702          * @return {Roo.dd.DDProxy} The DDProxy object
8703          */
8704         initDDProxy : function(group, config, overrides){
8705             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8706             return Roo.apply(dd, overrides);
8707         },
8708
8709         /**
8710          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8711          * @param {String} group The group the DDTarget object is member of
8712          * @param {Object} config The DDTarget config object
8713          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8714          * @return {Roo.dd.DDTarget} The DDTarget object
8715          */
8716         initDDTarget : function(group, config, overrides){
8717             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8718             return Roo.apply(dd, overrides);
8719         },
8720
8721         /**
8722          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8723          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8724          * @param {Boolean} visible Whether the element is visible
8725          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8726          * @return {Roo.Element} this
8727          */
8728          setVisible : function(visible, animate){
8729             if(!animate || !A){
8730                 if(this.visibilityMode == El.DISPLAY){
8731                     this.setDisplayed(visible);
8732                 }else{
8733                     this.fixDisplay();
8734                     this.dom.style.visibility = visible ? "visible" : "hidden";
8735                 }
8736             }else{
8737                 // closure for composites
8738                 var dom = this.dom;
8739                 var visMode = this.visibilityMode;
8740                 if(visible){
8741                     this.setOpacity(.01);
8742                     this.setVisible(true);
8743                 }
8744                 this.anim({opacity: { to: (visible?1:0) }},
8745                       this.preanim(arguments, 1),
8746                       null, .35, 'easeIn', function(){
8747                          if(!visible){
8748                              if(visMode == El.DISPLAY){
8749                                  dom.style.display = "none";
8750                              }else{
8751                                  dom.style.visibility = "hidden";
8752                              }
8753                              Roo.get(dom).setOpacity(1);
8754                          }
8755                      });
8756             }
8757             return this;
8758         },
8759
8760         /**
8761          * Returns true if display is not "none"
8762          * @return {Boolean}
8763          */
8764         isDisplayed : function() {
8765             return this.getStyle("display") != "none";
8766         },
8767
8768         /**
8769          * Toggles the element's visibility or display, depending on visibility mode.
8770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8771          * @return {Roo.Element} this
8772          */
8773         toggle : function(animate){
8774             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8775             return this;
8776         },
8777
8778         /**
8779          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8780          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8781          * @return {Roo.Element} this
8782          */
8783         setDisplayed : function(value) {
8784             if(typeof value == "boolean"){
8785                value = value ? this.originalDisplay : "none";
8786             }
8787             this.setStyle("display", value);
8788             return this;
8789         },
8790
8791         /**
8792          * Tries to focus the element. Any exceptions are caught and ignored.
8793          * @return {Roo.Element} this
8794          */
8795         focus : function() {
8796             try{
8797                 this.dom.focus();
8798             }catch(e){}
8799             return this;
8800         },
8801
8802         /**
8803          * Tries to blur the element. Any exceptions are caught and ignored.
8804          * @return {Roo.Element} this
8805          */
8806         blur : function() {
8807             try{
8808                 this.dom.blur();
8809             }catch(e){}
8810             return this;
8811         },
8812
8813         /**
8814          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8815          * @param {String/Array} className The CSS class to add, or an array of classes
8816          * @return {Roo.Element} this
8817          */
8818         addClass : function(className){
8819             if(className instanceof Array){
8820                 for(var i = 0, len = className.length; i < len; i++) {
8821                     this.addClass(className[i]);
8822                 }
8823             }else{
8824                 if(className && !this.hasClass(className)){
8825                     if (this.dom instanceof SVGElement) {
8826                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8827                     } else {
8828                         this.dom.className = this.dom.className + " " + className;
8829                     }
8830                 }
8831             }
8832             return this;
8833         },
8834
8835         /**
8836          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8837          * @param {String/Array} className The CSS class to add, or an array of classes
8838          * @return {Roo.Element} this
8839          */
8840         radioClass : function(className){
8841             var siblings = this.dom.parentNode.childNodes;
8842             for(var i = 0; i < siblings.length; i++) {
8843                 var s = siblings[i];
8844                 if(s.nodeType == 1){
8845                     Roo.get(s).removeClass(className);
8846                 }
8847             }
8848             this.addClass(className);
8849             return this;
8850         },
8851
8852         /**
8853          * Removes one or more CSS classes from the element.
8854          * @param {String/Array} className The CSS class to remove, or an array of classes
8855          * @return {Roo.Element} this
8856          */
8857         removeClass : function(className){
8858             
8859             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8860             if(!className || !cn){
8861                 return this;
8862             }
8863             if(className instanceof Array){
8864                 for(var i = 0, len = className.length; i < len; i++) {
8865                     this.removeClass(className[i]);
8866                 }
8867             }else{
8868                 if(this.hasClass(className)){
8869                     var re = this.classReCache[className];
8870                     if (!re) {
8871                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8872                        this.classReCache[className] = re;
8873                     }
8874                     if (this.dom instanceof SVGElement) {
8875                         this.dom.className.baseVal = cn.replace(re, " ");
8876                     } else {
8877                         this.dom.className = cn.replace(re, " ");
8878                     }
8879                 }
8880             }
8881             return this;
8882         },
8883
8884         // private
8885         classReCache: {},
8886
8887         /**
8888          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8889          * @param {String} className The CSS class to toggle
8890          * @return {Roo.Element} this
8891          */
8892         toggleClass : function(className){
8893             if(this.hasClass(className)){
8894                 this.removeClass(className);
8895             }else{
8896                 this.addClass(className);
8897             }
8898             return this;
8899         },
8900
8901         /**
8902          * Checks if the specified CSS class exists on this element's DOM node.
8903          * @param {String} className The CSS class to check for
8904          * @return {Boolean} True if the class exists, else false
8905          */
8906         hasClass : function(className){
8907             if (this.dom instanceof SVGElement) {
8908                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8909             } 
8910             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8911         },
8912
8913         /**
8914          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8915          * @param {String} oldClassName The CSS class to replace
8916          * @param {String} newClassName The replacement CSS class
8917          * @return {Roo.Element} this
8918          */
8919         replaceClass : function(oldClassName, newClassName){
8920             this.removeClass(oldClassName);
8921             this.addClass(newClassName);
8922             return this;
8923         },
8924
8925         /**
8926          * Returns an object with properties matching the styles requested.
8927          * For example, el.getStyles('color', 'font-size', 'width') might return
8928          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8929          * @param {String} style1 A style name
8930          * @param {String} style2 A style name
8931          * @param {String} etc.
8932          * @return {Object} The style object
8933          */
8934         getStyles : function(){
8935             var a = arguments, len = a.length, r = {};
8936             for(var i = 0; i < len; i++){
8937                 r[a[i]] = this.getStyle(a[i]);
8938             }
8939             return r;
8940         },
8941
8942         /**
8943          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8944          * @param {String} property The style property whose value is returned.
8945          * @return {String} The current value of the style property for this element.
8946          */
8947         getStyle : function(){
8948             return view && view.getComputedStyle ?
8949                 function(prop){
8950                     var el = this.dom, v, cs, camel;
8951                     if(prop == 'float'){
8952                         prop = "cssFloat";
8953                     }
8954                     if(el.style && (v = el.style[prop])){
8955                         return v;
8956                     }
8957                     if(cs = view.getComputedStyle(el, "")){
8958                         if(!(camel = propCache[prop])){
8959                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8960                         }
8961                         return cs[camel];
8962                     }
8963                     return null;
8964                 } :
8965                 function(prop){
8966                     var el = this.dom, v, cs, camel;
8967                     if(prop == 'opacity'){
8968                         if(typeof el.style.filter == 'string'){
8969                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8970                             if(m){
8971                                 var fv = parseFloat(m[1]);
8972                                 if(!isNaN(fv)){
8973                                     return fv ? fv / 100 : 0;
8974                                 }
8975                             }
8976                         }
8977                         return 1;
8978                     }else if(prop == 'float'){
8979                         prop = "styleFloat";
8980                     }
8981                     if(!(camel = propCache[prop])){
8982                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8983                     }
8984                     if(v = el.style[camel]){
8985                         return v;
8986                     }
8987                     if(cs = el.currentStyle){
8988                         return cs[camel];
8989                     }
8990                     return null;
8991                 };
8992         }(),
8993
8994         /**
8995          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8996          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8997          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8998          * @return {Roo.Element} this
8999          */
9000         setStyle : function(prop, value){
9001             if(typeof prop == "string"){
9002                 
9003                 if (prop == 'float') {
9004                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9005                     return this;
9006                 }
9007                 
9008                 var camel;
9009                 if(!(camel = propCache[prop])){
9010                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9011                 }
9012                 
9013                 if(camel == 'opacity') {
9014                     this.setOpacity(value);
9015                 }else{
9016                     this.dom.style[camel] = value;
9017                 }
9018             }else{
9019                 for(var style in prop){
9020                     if(typeof prop[style] != "function"){
9021                        this.setStyle(style, prop[style]);
9022                     }
9023                 }
9024             }
9025             return this;
9026         },
9027
9028         /**
9029          * More flexible version of {@link #setStyle} for setting style properties.
9030          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9031          * a function which returns such a specification.
9032          * @return {Roo.Element} this
9033          */
9034         applyStyles : function(style){
9035             Roo.DomHelper.applyStyles(this.dom, style);
9036             return this;
9037         },
9038
9039         /**
9040           * 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).
9041           * @return {Number} The X position of the element
9042           */
9043         getX : function(){
9044             return D.getX(this.dom);
9045         },
9046
9047         /**
9048           * 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).
9049           * @return {Number} The Y position of the element
9050           */
9051         getY : function(){
9052             return D.getY(this.dom);
9053         },
9054
9055         /**
9056           * 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).
9057           * @return {Array} The XY position of the element
9058           */
9059         getXY : function(){
9060             return D.getXY(this.dom);
9061         },
9062
9063         /**
9064          * 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).
9065          * @param {Number} The X position of the element
9066          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9067          * @return {Roo.Element} this
9068          */
9069         setX : function(x, animate){
9070             if(!animate || !A){
9071                 D.setX(this.dom, x);
9072             }else{
9073                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9074             }
9075             return this;
9076         },
9077
9078         /**
9079          * 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).
9080          * @param {Number} The Y position of the element
9081          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9082          * @return {Roo.Element} this
9083          */
9084         setY : function(y, animate){
9085             if(!animate || !A){
9086                 D.setY(this.dom, y);
9087             }else{
9088                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9089             }
9090             return this;
9091         },
9092
9093         /**
9094          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9095          * @param {String} left The left CSS property value
9096          * @return {Roo.Element} this
9097          */
9098         setLeft : function(left){
9099             this.setStyle("left", this.addUnits(left));
9100             return this;
9101         },
9102
9103         /**
9104          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9105          * @param {String} top The top CSS property value
9106          * @return {Roo.Element} this
9107          */
9108         setTop : function(top){
9109             this.setStyle("top", this.addUnits(top));
9110             return this;
9111         },
9112
9113         /**
9114          * Sets the element's CSS right style.
9115          * @param {String} right The right CSS property value
9116          * @return {Roo.Element} this
9117          */
9118         setRight : function(right){
9119             this.setStyle("right", this.addUnits(right));
9120             return this;
9121         },
9122
9123         /**
9124          * Sets the element's CSS bottom style.
9125          * @param {String} bottom The bottom CSS property value
9126          * @return {Roo.Element} this
9127          */
9128         setBottom : function(bottom){
9129             this.setStyle("bottom", this.addUnits(bottom));
9130             return this;
9131         },
9132
9133         /**
9134          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9135          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9136          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9137          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9138          * @return {Roo.Element} this
9139          */
9140         setXY : function(pos, animate){
9141             if(!animate || !A){
9142                 D.setXY(this.dom, pos);
9143             }else{
9144                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9145             }
9146             return this;
9147         },
9148
9149         /**
9150          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9151          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9152          * @param {Number} x X value for new position (coordinates are page-based)
9153          * @param {Number} y Y value for new position (coordinates are page-based)
9154          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9155          * @return {Roo.Element} this
9156          */
9157         setLocation : function(x, y, animate){
9158             this.setXY([x, y], this.preanim(arguments, 2));
9159             return this;
9160         },
9161
9162         /**
9163          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9164          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9165          * @param {Number} x X value for new position (coordinates are page-based)
9166          * @param {Number} y Y value for new position (coordinates are page-based)
9167          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9168          * @return {Roo.Element} this
9169          */
9170         moveTo : function(x, y, animate){
9171             this.setXY([x, y], this.preanim(arguments, 2));
9172             return this;
9173         },
9174
9175         /**
9176          * Returns the region of the given element.
9177          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9178          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9179          */
9180         getRegion : function(){
9181             return D.getRegion(this.dom);
9182         },
9183
9184         /**
9185          * Returns the offset height of the element
9186          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9187          * @return {Number} The element's height
9188          */
9189         getHeight : function(contentHeight){
9190             var h = this.dom.offsetHeight || 0;
9191             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9192         },
9193
9194         /**
9195          * Returns the offset width of the element
9196          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9197          * @return {Number} The element's width
9198          */
9199         getWidth : function(contentWidth){
9200             var w = this.dom.offsetWidth || 0;
9201             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9202         },
9203
9204         /**
9205          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9206          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9207          * if a height has not been set using CSS.
9208          * @return {Number}
9209          */
9210         getComputedHeight : function(){
9211             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9212             if(!h){
9213                 h = parseInt(this.getStyle('height'), 10) || 0;
9214                 if(!this.isBorderBox()){
9215                     h += this.getFrameWidth('tb');
9216                 }
9217             }
9218             return h;
9219         },
9220
9221         /**
9222          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9223          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9224          * if a width has not been set using CSS.
9225          * @return {Number}
9226          */
9227         getComputedWidth : function(){
9228             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9229             if(!w){
9230                 w = parseInt(this.getStyle('width'), 10) || 0;
9231                 if(!this.isBorderBox()){
9232                     w += this.getFrameWidth('lr');
9233                 }
9234             }
9235             return w;
9236         },
9237
9238         /**
9239          * Returns the size of the element.
9240          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9241          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9242          */
9243         getSize : function(contentSize){
9244             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9245         },
9246
9247         /**
9248          * Returns the width and height of the viewport.
9249          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9250          */
9251         getViewSize : function(){
9252             var d = this.dom, doc = document, aw = 0, ah = 0;
9253             if(d == doc || d == doc.body){
9254                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9255             }else{
9256                 return {
9257                     width : d.clientWidth,
9258                     height: d.clientHeight
9259                 };
9260             }
9261         },
9262
9263         /**
9264          * Returns the value of the "value" attribute
9265          * @param {Boolean} asNumber true to parse the value as a number
9266          * @return {String/Number}
9267          */
9268         getValue : function(asNumber){
9269             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9270         },
9271
9272         // private
9273         adjustWidth : function(width){
9274             if(typeof width == "number"){
9275                 if(this.autoBoxAdjust && !this.isBorderBox()){
9276                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9277                 }
9278                 if(width < 0){
9279                     width = 0;
9280                 }
9281             }
9282             return width;
9283         },
9284
9285         // private
9286         adjustHeight : function(height){
9287             if(typeof height == "number"){
9288                if(this.autoBoxAdjust && !this.isBorderBox()){
9289                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9290                }
9291                if(height < 0){
9292                    height = 0;
9293                }
9294             }
9295             return height;
9296         },
9297
9298         /**
9299          * Set the width of the element
9300          * @param {Number} width The new width
9301          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9302          * @return {Roo.Element} this
9303          */
9304         setWidth : function(width, animate){
9305             width = this.adjustWidth(width);
9306             if(!animate || !A){
9307                 this.dom.style.width = this.addUnits(width);
9308             }else{
9309                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9310             }
9311             return this;
9312         },
9313
9314         /**
9315          * Set the height of the element
9316          * @param {Number} height The new height
9317          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9318          * @return {Roo.Element} this
9319          */
9320          setHeight : function(height, animate){
9321             height = this.adjustHeight(height);
9322             if(!animate || !A){
9323                 this.dom.style.height = this.addUnits(height);
9324             }else{
9325                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9326             }
9327             return this;
9328         },
9329
9330         /**
9331          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9332          * @param {Number} width The new width
9333          * @param {Number} height The new height
9334          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9335          * @return {Roo.Element} this
9336          */
9337          setSize : function(width, height, animate){
9338             if(typeof width == "object"){ // in case of object from getSize()
9339                 height = width.height; width = width.width;
9340             }
9341             width = this.adjustWidth(width); height = this.adjustHeight(height);
9342             if(!animate || !A){
9343                 this.dom.style.width = this.addUnits(width);
9344                 this.dom.style.height = this.addUnits(height);
9345             }else{
9346                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9347             }
9348             return this;
9349         },
9350
9351         /**
9352          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9353          * @param {Number} x X value for new position (coordinates are page-based)
9354          * @param {Number} y Y value for new position (coordinates are page-based)
9355          * @param {Number} width The new width
9356          * @param {Number} height The new height
9357          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9358          * @return {Roo.Element} this
9359          */
9360         setBounds : function(x, y, width, height, animate){
9361             if(!animate || !A){
9362                 this.setSize(width, height);
9363                 this.setLocation(x, y);
9364             }else{
9365                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9366                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9367                               this.preanim(arguments, 4), 'motion');
9368             }
9369             return this;
9370         },
9371
9372         /**
9373          * 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.
9374          * @param {Roo.lib.Region} region The region to fill
9375          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9376          * @return {Roo.Element} this
9377          */
9378         setRegion : function(region, animate){
9379             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9380             return this;
9381         },
9382
9383         /**
9384          * Appends an event handler
9385          *
9386          * @param {String}   eventName     The type of event to append
9387          * @param {Function} fn        The method the event invokes
9388          * @param {Object} scope       (optional) The scope (this object) of the fn
9389          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9390          */
9391         addListener : function(eventName, fn, scope, options)
9392         {
9393             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9394                 this.addListener('touchstart', this.onTapHandler, this);
9395             }
9396             
9397             // we need to handle a special case where dom element is a svg element.
9398             // in this case we do not actua
9399             if (!this.dom) {
9400                 return;
9401             }
9402             
9403             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9404                 if (typeof(this.listeners[eventName]) == 'undefined') {
9405                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9406                 }
9407                 this.listeners[eventName].addListener(fn, scope, options);
9408                 return;
9409             }
9410             
9411                 
9412             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9413             
9414             
9415         },
9416         tapedTwice : false,
9417         onTapHandler : function(event)
9418         {
9419             if(!this.tapedTwice) {
9420                 this.tapedTwice = true;
9421                 var s = this;
9422                 setTimeout( function() {
9423                     s.tapedTwice = false;
9424                 }, 300 );
9425                 return;
9426             }
9427             event.preventDefault();
9428             var revent = new MouseEvent('dblclick',  {
9429                 view: window,
9430                 bubbles: true,
9431                 cancelable: true
9432             });
9433              
9434             this.dom.dispatchEvent(revent);
9435             //action on double tap goes below
9436              
9437         }, 
9438  
9439         /**
9440          * Removes an event handler from this element
9441          * @param {String} eventName the type of event to remove
9442          * @param {Function} fn the method the event invokes
9443          * @param {Function} scope (needed for svg fake listeners)
9444          * @return {Roo.Element} this
9445          */
9446         removeListener : function(eventName, fn, scope){
9447             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9448             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9449                 return this;
9450             }
9451             this.listeners[eventName].removeListener(fn, scope);
9452             return this;
9453         },
9454
9455         /**
9456          * Removes all previous added listeners from this element
9457          * @return {Roo.Element} this
9458          */
9459         removeAllListeners : function(){
9460             E.purgeElement(this.dom);
9461             this.listeners = {};
9462             return this;
9463         },
9464
9465         relayEvent : function(eventName, observable){
9466             this.on(eventName, function(e){
9467                 observable.fireEvent(eventName, e);
9468             });
9469         },
9470
9471         
9472         /**
9473          * Set the opacity of the element
9474          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9475          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9476          * @return {Roo.Element} this
9477          */
9478          setOpacity : function(opacity, animate){
9479             if(!animate || !A){
9480                 var s = this.dom.style;
9481                 if(Roo.isIE){
9482                     s.zoom = 1;
9483                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9484                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9485                 }else{
9486                     s.opacity = opacity;
9487                 }
9488             }else{
9489                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9490             }
9491             return this;
9492         },
9493
9494         /**
9495          * Gets the left X coordinate
9496          * @param {Boolean} local True to get the local css position instead of page coordinate
9497          * @return {Number}
9498          */
9499         getLeft : function(local){
9500             if(!local){
9501                 return this.getX();
9502             }else{
9503                 return parseInt(this.getStyle("left"), 10) || 0;
9504             }
9505         },
9506
9507         /**
9508          * Gets the right X coordinate of the element (element X position + element width)
9509          * @param {Boolean} local True to get the local css position instead of page coordinate
9510          * @return {Number}
9511          */
9512         getRight : function(local){
9513             if(!local){
9514                 return this.getX() + this.getWidth();
9515             }else{
9516                 return (this.getLeft(true) + this.getWidth()) || 0;
9517             }
9518         },
9519
9520         /**
9521          * Gets the top Y coordinate
9522          * @param {Boolean} local True to get the local css position instead of page coordinate
9523          * @return {Number}
9524          */
9525         getTop : function(local) {
9526             if(!local){
9527                 return this.getY();
9528             }else{
9529                 return parseInt(this.getStyle("top"), 10) || 0;
9530             }
9531         },
9532
9533         /**
9534          * Gets the bottom Y coordinate of the element (element Y position + element height)
9535          * @param {Boolean} local True to get the local css position instead of page coordinate
9536          * @return {Number}
9537          */
9538         getBottom : function(local){
9539             if(!local){
9540                 return this.getY() + this.getHeight();
9541             }else{
9542                 return (this.getTop(true) + this.getHeight()) || 0;
9543             }
9544         },
9545
9546         /**
9547         * Initializes positioning on this element. If a desired position is not passed, it will make the
9548         * the element positioned relative IF it is not already positioned.
9549         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9550         * @param {Number} zIndex (optional) The zIndex to apply
9551         * @param {Number} x (optional) Set the page X position
9552         * @param {Number} y (optional) Set the page Y position
9553         */
9554         position : function(pos, zIndex, x, y){
9555             if(!pos){
9556                if(this.getStyle('position') == 'static'){
9557                    this.setStyle('position', 'relative');
9558                }
9559             }else{
9560                 this.setStyle("position", pos);
9561             }
9562             if(zIndex){
9563                 this.setStyle("z-index", zIndex);
9564             }
9565             if(x !== undefined && y !== undefined){
9566                 this.setXY([x, y]);
9567             }else if(x !== undefined){
9568                 this.setX(x);
9569             }else if(y !== undefined){
9570                 this.setY(y);
9571             }
9572         },
9573
9574         /**
9575         * Clear positioning back to the default when the document was loaded
9576         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9577         * @return {Roo.Element} this
9578          */
9579         clearPositioning : function(value){
9580             value = value ||'';
9581             this.setStyle({
9582                 "left": value,
9583                 "right": value,
9584                 "top": value,
9585                 "bottom": value,
9586                 "z-index": "",
9587                 "position" : "static"
9588             });
9589             return this;
9590         },
9591
9592         /**
9593         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9594         * snapshot before performing an update and then restoring the element.
9595         * @return {Object}
9596         */
9597         getPositioning : function(){
9598             var l = this.getStyle("left");
9599             var t = this.getStyle("top");
9600             return {
9601                 "position" : this.getStyle("position"),
9602                 "left" : l,
9603                 "right" : l ? "" : this.getStyle("right"),
9604                 "top" : t,
9605                 "bottom" : t ? "" : this.getStyle("bottom"),
9606                 "z-index" : this.getStyle("z-index")
9607             };
9608         },
9609
9610         /**
9611          * Gets the width of the border(s) for the specified side(s)
9612          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9613          * passing lr would get the border (l)eft width + the border (r)ight width.
9614          * @return {Number} The width of the sides passed added together
9615          */
9616         getBorderWidth : function(side){
9617             return this.addStyles(side, El.borders);
9618         },
9619
9620         /**
9621          * Gets the width of the padding(s) for the specified side(s)
9622          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9623          * passing lr would get the padding (l)eft + the padding (r)ight.
9624          * @return {Number} The padding of the sides passed added together
9625          */
9626         getPadding : function(side){
9627             return this.addStyles(side, El.paddings);
9628         },
9629
9630         /**
9631         * Set positioning with an object returned by getPositioning().
9632         * @param {Object} posCfg
9633         * @return {Roo.Element} this
9634          */
9635         setPositioning : function(pc){
9636             this.applyStyles(pc);
9637             if(pc.right == "auto"){
9638                 this.dom.style.right = "";
9639             }
9640             if(pc.bottom == "auto"){
9641                 this.dom.style.bottom = "";
9642             }
9643             return this;
9644         },
9645
9646         // private
9647         fixDisplay : function(){
9648             if(this.getStyle("display") == "none"){
9649                 this.setStyle("visibility", "hidden");
9650                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9651                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9652                     this.setStyle("display", "block");
9653                 }
9654             }
9655         },
9656
9657         /**
9658          * Quick set left and top adding default units
9659          * @param {String} left The left CSS property value
9660          * @param {String} top The top CSS property value
9661          * @return {Roo.Element} this
9662          */
9663          setLeftTop : function(left, top){
9664             this.dom.style.left = this.addUnits(left);
9665             this.dom.style.top = this.addUnits(top);
9666             return this;
9667         },
9668
9669         /**
9670          * Move this element relative to its current position.
9671          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9672          * @param {Number} distance How far to move the element in pixels
9673          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9674          * @return {Roo.Element} this
9675          */
9676          move : function(direction, distance, animate){
9677             var xy = this.getXY();
9678             direction = direction.toLowerCase();
9679             switch(direction){
9680                 case "l":
9681                 case "left":
9682                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9683                     break;
9684                case "r":
9685                case "right":
9686                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9687                     break;
9688                case "t":
9689                case "top":
9690                case "up":
9691                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9692                     break;
9693                case "b":
9694                case "bottom":
9695                case "down":
9696                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9697                     break;
9698             }
9699             return this;
9700         },
9701
9702         /**
9703          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9704          * @return {Roo.Element} this
9705          */
9706         clip : function(){
9707             if(!this.isClipped){
9708                this.isClipped = true;
9709                this.originalClip = {
9710                    "o": this.getStyle("overflow"),
9711                    "x": this.getStyle("overflow-x"),
9712                    "y": this.getStyle("overflow-y")
9713                };
9714                this.setStyle("overflow", "hidden");
9715                this.setStyle("overflow-x", "hidden");
9716                this.setStyle("overflow-y", "hidden");
9717             }
9718             return this;
9719         },
9720
9721         /**
9722          *  Return clipping (overflow) to original clipping before clip() was called
9723          * @return {Roo.Element} this
9724          */
9725         unclip : function(){
9726             if(this.isClipped){
9727                 this.isClipped = false;
9728                 var o = this.originalClip;
9729                 if(o.o){this.setStyle("overflow", o.o);}
9730                 if(o.x){this.setStyle("overflow-x", o.x);}
9731                 if(o.y){this.setStyle("overflow-y", o.y);}
9732             }
9733             return this;
9734         },
9735
9736
9737         /**
9738          * Gets the x,y coordinates specified by the anchor position on the element.
9739          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9740          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9741          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9742          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9743          * @return {Array} [x, y] An array containing the element's x and y coordinates
9744          */
9745         getAnchorXY : function(anchor, local, s){
9746             //Passing a different size is useful for pre-calculating anchors,
9747             //especially for anchored animations that change the el size.
9748
9749             var w, h, vp = false;
9750             if(!s){
9751                 var d = this.dom;
9752                 if(d == document.body || d == document){
9753                     vp = true;
9754                     w = D.getViewWidth(); h = D.getViewHeight();
9755                 }else{
9756                     w = this.getWidth(); h = this.getHeight();
9757                 }
9758             }else{
9759                 w = s.width;  h = s.height;
9760             }
9761             var x = 0, y = 0, r = Math.round;
9762             switch((anchor || "tl").toLowerCase()){
9763                 case "c":
9764                     x = r(w*.5);
9765                     y = r(h*.5);
9766                 break;
9767                 case "t":
9768                     x = r(w*.5);
9769                     y = 0;
9770                 break;
9771                 case "l":
9772                     x = 0;
9773                     y = r(h*.5);
9774                 break;
9775                 case "r":
9776                     x = w;
9777                     y = r(h*.5);
9778                 break;
9779                 case "b":
9780                     x = r(w*.5);
9781                     y = h;
9782                 break;
9783                 case "tl":
9784                     x = 0;
9785                     y = 0;
9786                 break;
9787                 case "bl":
9788                     x = 0;
9789                     y = h;
9790                 break;
9791                 case "br":
9792                     x = w;
9793                     y = h;
9794                 break;
9795                 case "tr":
9796                     x = w;
9797                     y = 0;
9798                 break;
9799             }
9800             if(local === true){
9801                 return [x, y];
9802             }
9803             if(vp){
9804                 var sc = this.getScroll();
9805                 return [x + sc.left, y + sc.top];
9806             }
9807             //Add the element's offset xy
9808             var o = this.getXY();
9809             return [x+o[0], y+o[1]];
9810         },
9811
9812         /**
9813          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9814          * supported position values.
9815          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9816          * @param {String} position The position to align to.
9817          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9818          * @return {Array} [x, y]
9819          */
9820         getAlignToXY : function(el, p, o)
9821         {
9822             el = Roo.get(el);
9823             var d = this.dom;
9824             if(!el.dom){
9825                 throw "Element.alignTo with an element that doesn't exist";
9826             }
9827             var c = false; //constrain to viewport
9828             var p1 = "", p2 = "";
9829             o = o || [0,0];
9830
9831             if(!p){
9832                 p = "tl-bl";
9833             }else if(p == "?"){
9834                 p = "tl-bl?";
9835             }else if(p.indexOf("-") == -1){
9836                 p = "tl-" + p;
9837             }
9838             p = p.toLowerCase();
9839             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9840             if(!m){
9841                throw "Element.alignTo with an invalid alignment " + p;
9842             }
9843             p1 = m[1]; p2 = m[2]; c = !!m[3];
9844
9845             //Subtract the aligned el's internal xy from the target's offset xy
9846             //plus custom offset to get the aligned el's new offset xy
9847             var a1 = this.getAnchorXY(p1, true);
9848             var a2 = el.getAnchorXY(p2, false);
9849             var x = a2[0] - a1[0] + o[0];
9850             var y = a2[1] - a1[1] + o[1];
9851             if(c){
9852                 //constrain the aligned el to viewport if necessary
9853                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9854                 // 5px of margin for ie
9855                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9856
9857                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9858                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9859                 //otherwise swap the aligned el to the opposite border of the target.
9860                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9861                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9862                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9863                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9864
9865                var doc = document;
9866                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9867                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9868
9869                if((x+w) > dw + scrollX){
9870                     x = swapX ? r.left-w : dw+scrollX-w;
9871                 }
9872                if(x < scrollX){
9873                    x = swapX ? r.right : scrollX;
9874                }
9875                if((y+h) > dh + scrollY){
9876                     y = swapY ? r.top-h : dh+scrollY-h;
9877                 }
9878                if (y < scrollY){
9879                    y = swapY ? r.bottom : scrollY;
9880                }
9881             }
9882             return [x,y];
9883         },
9884
9885         // private
9886         getConstrainToXY : function(){
9887             var os = {top:0, left:0, bottom:0, right: 0};
9888
9889             return function(el, local, offsets, proposedXY){
9890                 el = Roo.get(el);
9891                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9892
9893                 var vw, vh, vx = 0, vy = 0;
9894                 if(el.dom == document.body || el.dom == document){
9895                     vw = Roo.lib.Dom.getViewWidth();
9896                     vh = Roo.lib.Dom.getViewHeight();
9897                 }else{
9898                     vw = el.dom.clientWidth;
9899                     vh = el.dom.clientHeight;
9900                     if(!local){
9901                         var vxy = el.getXY();
9902                         vx = vxy[0];
9903                         vy = vxy[1];
9904                     }
9905                 }
9906
9907                 var s = el.getScroll();
9908
9909                 vx += offsets.left + s.left;
9910                 vy += offsets.top + s.top;
9911
9912                 vw -= offsets.right;
9913                 vh -= offsets.bottom;
9914
9915                 var vr = vx+vw;
9916                 var vb = vy+vh;
9917
9918                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9919                 var x = xy[0], y = xy[1];
9920                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9921
9922                 // only move it if it needs it
9923                 var moved = false;
9924
9925                 // first validate right/bottom
9926                 if((x + w) > vr){
9927                     x = vr - w;
9928                     moved = true;
9929                 }
9930                 if((y + h) > vb){
9931                     y = vb - h;
9932                     moved = true;
9933                 }
9934                 // then make sure top/left isn't negative
9935                 if(x < vx){
9936                     x = vx;
9937                     moved = true;
9938                 }
9939                 if(y < vy){
9940                     y = vy;
9941                     moved = true;
9942                 }
9943                 return moved ? [x, y] : false;
9944             };
9945         }(),
9946
9947         // private
9948         adjustForConstraints : function(xy, parent, offsets){
9949             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9950         },
9951
9952         /**
9953          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9954          * document it aligns it to the viewport.
9955          * The position parameter is optional, and can be specified in any one of the following formats:
9956          * <ul>
9957          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9958          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9959          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9960          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9961          *   <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
9962          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9963          * </ul>
9964          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9965          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9966          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9967          * that specified in order to enforce the viewport constraints.
9968          * Following are all of the supported anchor positions:
9969     <pre>
9970     Value  Description
9971     -----  -----------------------------
9972     tl     The top left corner (default)
9973     t      The center of the top edge
9974     tr     The top right corner
9975     l      The center of the left edge
9976     c      In the center of the element
9977     r      The center of the right edge
9978     bl     The bottom left corner
9979     b      The center of the bottom edge
9980     br     The bottom right corner
9981     </pre>
9982     Example Usage:
9983     <pre><code>
9984     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9985     el.alignTo("other-el");
9986
9987     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9988     el.alignTo("other-el", "tr?");
9989
9990     // align the bottom right corner of el with the center left edge of other-el
9991     el.alignTo("other-el", "br-l?");
9992
9993     // align the center of el with the bottom left corner of other-el and
9994     // adjust the x position by -6 pixels (and the y position by 0)
9995     el.alignTo("other-el", "c-bl", [-6, 0]);
9996     </code></pre>
9997          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9998          * @param {String} position The position to align to.
9999          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10000          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10001          * @return {Roo.Element} this
10002          */
10003         alignTo : function(element, position, offsets, animate){
10004             var xy = this.getAlignToXY(element, position, offsets);
10005             this.setXY(xy, this.preanim(arguments, 3));
10006             return this;
10007         },
10008
10009         /**
10010          * Anchors an element to another element and realigns it when the window is resized.
10011          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10012          * @param {String} position The position to align to.
10013          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10014          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10015          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10016          * is a number, it is used as the buffer delay (defaults to 50ms).
10017          * @param {Function} callback The function to call after the animation finishes
10018          * @return {Roo.Element} this
10019          */
10020         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10021             var action = function(){
10022                 this.alignTo(el, alignment, offsets, animate);
10023                 Roo.callback(callback, this);
10024             };
10025             Roo.EventManager.onWindowResize(action, this);
10026             var tm = typeof monitorScroll;
10027             if(tm != 'undefined'){
10028                 Roo.EventManager.on(window, 'scroll', action, this,
10029                     {buffer: tm == 'number' ? monitorScroll : 50});
10030             }
10031             action.call(this); // align immediately
10032             return this;
10033         },
10034         /**
10035          * Clears any opacity settings from this element. Required in some cases for IE.
10036          * @return {Roo.Element} this
10037          */
10038         clearOpacity : function(){
10039             if (window.ActiveXObject) {
10040                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10041                     this.dom.style.filter = "";
10042                 }
10043             } else {
10044                 this.dom.style.opacity = "";
10045                 this.dom.style["-moz-opacity"] = "";
10046                 this.dom.style["-khtml-opacity"] = "";
10047             }
10048             return this;
10049         },
10050
10051         /**
10052          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10053          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10054          * @return {Roo.Element} this
10055          */
10056         hide : function(animate){
10057             this.setVisible(false, this.preanim(arguments, 0));
10058             return this;
10059         },
10060
10061         /**
10062         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10063         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10064          * @return {Roo.Element} this
10065          */
10066         show : function(animate){
10067             this.setVisible(true, this.preanim(arguments, 0));
10068             return this;
10069         },
10070
10071         /**
10072          * @private Test if size has a unit, otherwise appends the default
10073          */
10074         addUnits : function(size){
10075             return Roo.Element.addUnits(size, this.defaultUnit);
10076         },
10077
10078         /**
10079          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10080          * @return {Roo.Element} this
10081          */
10082         beginMeasure : function(){
10083             var el = this.dom;
10084             if(el.offsetWidth || el.offsetHeight){
10085                 return this; // offsets work already
10086             }
10087             var changed = [];
10088             var p = this.dom, b = document.body; // start with this element
10089             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10090                 var pe = Roo.get(p);
10091                 if(pe.getStyle('display') == 'none'){
10092                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10093                     p.style.visibility = "hidden";
10094                     p.style.display = "block";
10095                 }
10096                 p = p.parentNode;
10097             }
10098             this._measureChanged = changed;
10099             return this;
10100
10101         },
10102
10103         /**
10104          * Restores displays to before beginMeasure was called
10105          * @return {Roo.Element} this
10106          */
10107         endMeasure : function(){
10108             var changed = this._measureChanged;
10109             if(changed){
10110                 for(var i = 0, len = changed.length; i < len; i++) {
10111                     var r = changed[i];
10112                     r.el.style.visibility = r.visibility;
10113                     r.el.style.display = "none";
10114                 }
10115                 this._measureChanged = null;
10116             }
10117             return this;
10118         },
10119
10120         /**
10121         * Update the innerHTML of this element, optionally searching for and processing scripts
10122         * @param {String} html The new HTML
10123         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10124         * @param {Function} callback For async script loading you can be noticed when the update completes
10125         * @return {Roo.Element} this
10126          */
10127         update : function(html, loadScripts, callback){
10128             if(typeof html == "undefined"){
10129                 html = "";
10130             }
10131             if(loadScripts !== true){
10132                 this.dom.innerHTML = html;
10133                 if(typeof callback == "function"){
10134                     callback();
10135                 }
10136                 return this;
10137             }
10138             var id = Roo.id();
10139             var dom = this.dom;
10140
10141             html += '<span id="' + id + '"></span>';
10142
10143             E.onAvailable(id, function(){
10144                 var hd = document.getElementsByTagName("head")[0];
10145                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10146                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10147                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10148
10149                 var match;
10150                 while(match = re.exec(html)){
10151                     var attrs = match[1];
10152                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10153                     if(srcMatch && srcMatch[2]){
10154                        var s = document.createElement("script");
10155                        s.src = srcMatch[2];
10156                        var typeMatch = attrs.match(typeRe);
10157                        if(typeMatch && typeMatch[2]){
10158                            s.type = typeMatch[2];
10159                        }
10160                        hd.appendChild(s);
10161                     }else if(match[2] && match[2].length > 0){
10162                         if(window.execScript) {
10163                            window.execScript(match[2]);
10164                         } else {
10165                             /**
10166                              * eval:var:id
10167                              * eval:var:dom
10168                              * eval:var:html
10169                              * 
10170                              */
10171                            window.eval(match[2]);
10172                         }
10173                     }
10174                 }
10175                 var el = document.getElementById(id);
10176                 if(el){el.parentNode.removeChild(el);}
10177                 if(typeof callback == "function"){
10178                     callback();
10179                 }
10180             });
10181             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10182             return this;
10183         },
10184
10185         /**
10186          * Direct access to the UpdateManager update() method (takes the same parameters).
10187          * @param {String/Function} url The url for this request or a function to call to get the url
10188          * @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}
10189          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10190          * @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.
10191          * @return {Roo.Element} this
10192          */
10193         load : function(){
10194             var um = this.getUpdateManager();
10195             um.update.apply(um, arguments);
10196             return this;
10197         },
10198
10199         /**
10200         * Gets this element's UpdateManager
10201         * @return {Roo.UpdateManager} The UpdateManager
10202         */
10203         getUpdateManager : function(){
10204             if(!this.updateManager){
10205                 this.updateManager = new Roo.UpdateManager(this);
10206             }
10207             return this.updateManager;
10208         },
10209
10210         /**
10211          * Disables text selection for this element (normalized across browsers)
10212          * @return {Roo.Element} this
10213          */
10214         unselectable : function(){
10215             this.dom.unselectable = "on";
10216             this.swallowEvent("selectstart", true);
10217             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10218             this.addClass("x-unselectable");
10219             return this;
10220         },
10221
10222         /**
10223         * Calculates the x, y to center this element on the screen
10224         * @return {Array} The x, y values [x, y]
10225         */
10226         getCenterXY : function(){
10227             return this.getAlignToXY(document, 'c-c');
10228         },
10229
10230         /**
10231         * Centers the Element in either the viewport, or another Element.
10232         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10233         */
10234         center : function(centerIn){
10235             this.alignTo(centerIn || document, 'c-c');
10236             return this;
10237         },
10238
10239         /**
10240          * Tests various css rules/browsers to determine if this element uses a border box
10241          * @return {Boolean}
10242          */
10243         isBorderBox : function(){
10244             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10245         },
10246
10247         /**
10248          * Return a box {x, y, width, height} that can be used to set another elements
10249          * size/location to match this element.
10250          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10251          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10252          * @return {Object} box An object in the format {x, y, width, height}
10253          */
10254         getBox : function(contentBox, local){
10255             var xy;
10256             if(!local){
10257                 xy = this.getXY();
10258             }else{
10259                 var left = parseInt(this.getStyle("left"), 10) || 0;
10260                 var top = parseInt(this.getStyle("top"), 10) || 0;
10261                 xy = [left, top];
10262             }
10263             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10264             if(!contentBox){
10265                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10266             }else{
10267                 var l = this.getBorderWidth("l")+this.getPadding("l");
10268                 var r = this.getBorderWidth("r")+this.getPadding("r");
10269                 var t = this.getBorderWidth("t")+this.getPadding("t");
10270                 var b = this.getBorderWidth("b")+this.getPadding("b");
10271                 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)};
10272             }
10273             bx.right = bx.x + bx.width;
10274             bx.bottom = bx.y + bx.height;
10275             return bx;
10276         },
10277
10278         /**
10279          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10280          for more information about the sides.
10281          * @param {String} sides
10282          * @return {Number}
10283          */
10284         getFrameWidth : function(sides, onlyContentBox){
10285             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10286         },
10287
10288         /**
10289          * 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.
10290          * @param {Object} box The box to fill {x, y, width, height}
10291          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10292          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10293          * @return {Roo.Element} this
10294          */
10295         setBox : function(box, adjust, animate){
10296             var w = box.width, h = box.height;
10297             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10298                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10299                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10300             }
10301             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10302             return this;
10303         },
10304
10305         /**
10306          * Forces the browser to repaint this element
10307          * @return {Roo.Element} this
10308          */
10309          repaint : function(){
10310             var dom = this.dom;
10311             this.addClass("x-repaint");
10312             setTimeout(function(){
10313                 Roo.get(dom).removeClass("x-repaint");
10314             }, 1);
10315             return this;
10316         },
10317
10318         /**
10319          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10320          * then it returns the calculated width of the sides (see getPadding)
10321          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10322          * @return {Object/Number}
10323          */
10324         getMargins : function(side){
10325             if(!side){
10326                 return {
10327                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10328                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10329                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10330                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10331                 };
10332             }else{
10333                 return this.addStyles(side, El.margins);
10334              }
10335         },
10336
10337         // private
10338         addStyles : function(sides, styles){
10339             var val = 0, v, w;
10340             for(var i = 0, len = sides.length; i < len; i++){
10341                 v = this.getStyle(styles[sides.charAt(i)]);
10342                 if(v){
10343                      w = parseInt(v, 10);
10344                      if(w){ val += w; }
10345                 }
10346             }
10347             return val;
10348         },
10349
10350         /**
10351          * Creates a proxy element of this element
10352          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10353          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10354          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10355          * @return {Roo.Element} The new proxy element
10356          */
10357         createProxy : function(config, renderTo, matchBox){
10358             if(renderTo){
10359                 renderTo = Roo.getDom(renderTo);
10360             }else{
10361                 renderTo = document.body;
10362             }
10363             config = typeof config == "object" ?
10364                 config : {tag : "div", cls: config};
10365             var proxy = Roo.DomHelper.append(renderTo, config, true);
10366             if(matchBox){
10367                proxy.setBox(this.getBox());
10368             }
10369             return proxy;
10370         },
10371
10372         /**
10373          * Puts a mask over this element to disable user interaction. Requires core.css.
10374          * This method can only be applied to elements which accept child nodes.
10375          * @param {String} msg (optional) A message to display in the mask
10376          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10377          * @return {Element} The mask  element
10378          */
10379         mask : function(msg, msgCls)
10380         {
10381             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10382                 this.setStyle("position", "relative");
10383             }
10384             if(!this._mask){
10385                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10386             }
10387             
10388             this.addClass("x-masked");
10389             this._mask.setDisplayed(true);
10390             
10391             // we wander
10392             var z = 0;
10393             var dom = this.dom;
10394             while (dom && dom.style) {
10395                 if (!isNaN(parseInt(dom.style.zIndex))) {
10396                     z = Math.max(z, parseInt(dom.style.zIndex));
10397                 }
10398                 dom = dom.parentNode;
10399             }
10400             // if we are masking the body - then it hides everything..
10401             if (this.dom == document.body) {
10402                 z = 1000000;
10403                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10404                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10405             }
10406            
10407             if(typeof msg == 'string'){
10408                 if(!this._maskMsg){
10409                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10410                         cls: "roo-el-mask-msg", 
10411                         cn: [
10412                             {
10413                                 tag: 'i',
10414                                 cls: 'fa fa-spinner fa-spin'
10415                             },
10416                             {
10417                                 tag: 'div'
10418                             }   
10419                         ]
10420                     }, true);
10421                 }
10422                 var mm = this._maskMsg;
10423                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10424                 if (mm.dom.lastChild) { // weird IE issue?
10425                     mm.dom.lastChild.innerHTML = msg;
10426                 }
10427                 mm.setDisplayed(true);
10428                 mm.center(this);
10429                 mm.setStyle('z-index', z + 102);
10430             }
10431             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10432                 this._mask.setHeight(this.getHeight());
10433             }
10434             this._mask.setStyle('z-index', z + 100);
10435             
10436             return this._mask;
10437         },
10438
10439         /**
10440          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10441          * it is cached for reuse.
10442          */
10443         unmask : function(removeEl){
10444             if(this._mask){
10445                 if(removeEl === true){
10446                     this._mask.remove();
10447                     delete this._mask;
10448                     if(this._maskMsg){
10449                         this._maskMsg.remove();
10450                         delete this._maskMsg;
10451                     }
10452                 }else{
10453                     this._mask.setDisplayed(false);
10454                     if(this._maskMsg){
10455                         this._maskMsg.setDisplayed(false);
10456                     }
10457                 }
10458             }
10459             this.removeClass("x-masked");
10460         },
10461
10462         /**
10463          * Returns true if this element is masked
10464          * @return {Boolean}
10465          */
10466         isMasked : function(){
10467             return this._mask && this._mask.isVisible();
10468         },
10469
10470         /**
10471          * Creates an iframe shim for this element to keep selects and other windowed objects from
10472          * showing through.
10473          * @return {Roo.Element} The new shim element
10474          */
10475         createShim : function(){
10476             var el = document.createElement('iframe');
10477             el.frameBorder = 'no';
10478             el.className = 'roo-shim';
10479             if(Roo.isIE && Roo.isSecure){
10480                 el.src = Roo.SSL_SECURE_URL;
10481             }
10482             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10483             shim.autoBoxAdjust = false;
10484             return shim;
10485         },
10486
10487         /**
10488          * Removes this element from the DOM and deletes it from the cache
10489          */
10490         remove : function(){
10491             if(this.dom.parentNode){
10492                 this.dom.parentNode.removeChild(this.dom);
10493             }
10494             delete El.cache[this.dom.id];
10495         },
10496
10497         /**
10498          * Sets up event handlers to add and remove a css class when the mouse is over this element
10499          * @param {String} className
10500          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10501          * mouseout events for children elements
10502          * @return {Roo.Element} this
10503          */
10504         addClassOnOver : function(className, preventFlicker){
10505             this.on("mouseover", function(){
10506                 Roo.fly(this, '_internal').addClass(className);
10507             }, this.dom);
10508             var removeFn = function(e){
10509                 if(preventFlicker !== true || !e.within(this, true)){
10510                     Roo.fly(this, '_internal').removeClass(className);
10511                 }
10512             };
10513             this.on("mouseout", removeFn, this.dom);
10514             return this;
10515         },
10516
10517         /**
10518          * Sets up event handlers to add and remove a css class when this element has the focus
10519          * @param {String} className
10520          * @return {Roo.Element} this
10521          */
10522         addClassOnFocus : function(className){
10523             this.on("focus", function(){
10524                 Roo.fly(this, '_internal').addClass(className);
10525             }, this.dom);
10526             this.on("blur", function(){
10527                 Roo.fly(this, '_internal').removeClass(className);
10528             }, this.dom);
10529             return this;
10530         },
10531         /**
10532          * 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)
10533          * @param {String} className
10534          * @return {Roo.Element} this
10535          */
10536         addClassOnClick : function(className){
10537             var dom = this.dom;
10538             this.on("mousedown", function(){
10539                 Roo.fly(dom, '_internal').addClass(className);
10540                 var d = Roo.get(document);
10541                 var fn = function(){
10542                     Roo.fly(dom, '_internal').removeClass(className);
10543                     d.removeListener("mouseup", fn);
10544                 };
10545                 d.on("mouseup", fn);
10546             });
10547             return this;
10548         },
10549
10550         /**
10551          * Stops the specified event from bubbling and optionally prevents the default action
10552          * @param {String} eventName
10553          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10554          * @return {Roo.Element} this
10555          */
10556         swallowEvent : function(eventName, preventDefault){
10557             var fn = function(e){
10558                 e.stopPropagation();
10559                 if(preventDefault){
10560                     e.preventDefault();
10561                 }
10562             };
10563             if(eventName instanceof Array){
10564                 for(var i = 0, len = eventName.length; i < len; i++){
10565                      this.on(eventName[i], fn);
10566                 }
10567                 return this;
10568             }
10569             this.on(eventName, fn);
10570             return this;
10571         },
10572
10573         /**
10574          * @private
10575          */
10576         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10577
10578         /**
10579          * Sizes this element to its parent element's dimensions performing
10580          * neccessary box adjustments.
10581          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10582          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10583          * @return {Roo.Element} this
10584          */
10585         fitToParent : function(monitorResize, targetParent) {
10586           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10587           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10588           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10589             return this;
10590           }
10591           var p = Roo.get(targetParent || this.dom.parentNode);
10592           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10593           if (monitorResize === true) {
10594             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10595             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10596           }
10597           return this;
10598         },
10599
10600         /**
10601          * Gets the next sibling, skipping text nodes
10602          * @return {HTMLElement} The next sibling or null
10603          */
10604         getNextSibling : function(){
10605             var n = this.dom.nextSibling;
10606             while(n && n.nodeType != 1){
10607                 n = n.nextSibling;
10608             }
10609             return n;
10610         },
10611
10612         /**
10613          * Gets the previous sibling, skipping text nodes
10614          * @return {HTMLElement} The previous sibling or null
10615          */
10616         getPrevSibling : function(){
10617             var n = this.dom.previousSibling;
10618             while(n && n.nodeType != 1){
10619                 n = n.previousSibling;
10620             }
10621             return n;
10622         },
10623
10624
10625         /**
10626          * Appends the passed element(s) to this element
10627          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10628          * @return {Roo.Element} this
10629          */
10630         appendChild: function(el){
10631             el = Roo.get(el);
10632             el.appendTo(this);
10633             return this;
10634         },
10635
10636         /**
10637          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10638          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10639          * automatically generated with the specified attributes.
10640          * @param {HTMLElement} insertBefore (optional) a child element of this element
10641          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10642          * @return {Roo.Element} The new child element
10643          */
10644         createChild: function(config, insertBefore, returnDom){
10645             config = config || {tag:'div'};
10646             if(insertBefore){
10647                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10648             }
10649             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10650         },
10651
10652         /**
10653          * Appends this element to the passed element
10654          * @param {String/HTMLElement/Element} el The new parent element
10655          * @return {Roo.Element} this
10656          */
10657         appendTo: function(el){
10658             el = Roo.getDom(el);
10659             el.appendChild(this.dom);
10660             return this;
10661         },
10662
10663         /**
10664          * Inserts this element before the passed element in the DOM
10665          * @param {String/HTMLElement/Element} el The element to insert before
10666          * @return {Roo.Element} this
10667          */
10668         insertBefore: function(el){
10669             el = Roo.getDom(el);
10670             el.parentNode.insertBefore(this.dom, el);
10671             return this;
10672         },
10673
10674         /**
10675          * Inserts this element after the passed element in the DOM
10676          * @param {String/HTMLElement/Element} el The element to insert after
10677          * @return {Roo.Element} this
10678          */
10679         insertAfter: function(el){
10680             el = Roo.getDom(el);
10681             el.parentNode.insertBefore(this.dom, el.nextSibling);
10682             return this;
10683         },
10684
10685         /**
10686          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10687          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10688          * @return {Roo.Element} The new child
10689          */
10690         insertFirst: function(el, returnDom){
10691             el = el || {};
10692             if(typeof el == 'object' && !el.nodeType){ // dh config
10693                 return this.createChild(el, this.dom.firstChild, returnDom);
10694             }else{
10695                 el = Roo.getDom(el);
10696                 this.dom.insertBefore(el, this.dom.firstChild);
10697                 return !returnDom ? Roo.get(el) : el;
10698             }
10699         },
10700
10701         /**
10702          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10703          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10704          * @param {String} where (optional) 'before' or 'after' defaults to before
10705          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10706          * @return {Roo.Element} the inserted Element
10707          */
10708         insertSibling: function(el, where, returnDom){
10709             where = where ? where.toLowerCase() : 'before';
10710             el = el || {};
10711             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10712
10713             if(typeof el == 'object' && !el.nodeType){ // dh config
10714                 if(where == 'after' && !this.dom.nextSibling){
10715                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10716                 }else{
10717                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10718                 }
10719
10720             }else{
10721                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10722                             where == 'before' ? this.dom : this.dom.nextSibling);
10723                 if(!returnDom){
10724                     rt = Roo.get(rt);
10725                 }
10726             }
10727             return rt;
10728         },
10729
10730         /**
10731          * Creates and wraps this element with another element
10732          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10733          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10734          * @return {HTMLElement/Element} The newly created wrapper element
10735          */
10736         wrap: function(config, returnDom){
10737             if(!config){
10738                 config = {tag: "div"};
10739             }
10740             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10741             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10742             return newEl;
10743         },
10744
10745         /**
10746          * Replaces the passed element with this element
10747          * @param {String/HTMLElement/Element} el The element to replace
10748          * @return {Roo.Element} this
10749          */
10750         replace: function(el){
10751             el = Roo.get(el);
10752             this.insertBefore(el);
10753             el.remove();
10754             return this;
10755         },
10756
10757         /**
10758          * Inserts an html fragment into this element
10759          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10760          * @param {String} html The HTML fragment
10761          * @param {Boolean} returnEl True to return an Roo.Element
10762          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10763          */
10764         insertHtml : function(where, html, returnEl){
10765             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10766             return returnEl ? Roo.get(el) : el;
10767         },
10768
10769         /**
10770          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10771          * @param {Object} o The object with the attributes
10772          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10773          * @return {Roo.Element} this
10774          */
10775         set : function(o, useSet){
10776             var el = this.dom;
10777             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10778             for(var attr in o){
10779                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10780                 if(attr=="cls"){
10781                     el.className = o["cls"];
10782                 }else{
10783                     if(useSet) {
10784                         el.setAttribute(attr, o[attr]);
10785                     } else {
10786                         el[attr] = o[attr];
10787                     }
10788                 }
10789             }
10790             if(o.style){
10791                 Roo.DomHelper.applyStyles(el, o.style);
10792             }
10793             return this;
10794         },
10795
10796         /**
10797          * Convenience method for constructing a KeyMap
10798          * @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:
10799          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10800          * @param {Function} fn The function to call
10801          * @param {Object} scope (optional) The scope of the function
10802          * @return {Roo.KeyMap} The KeyMap created
10803          */
10804         addKeyListener : function(key, fn, scope){
10805             var config;
10806             if(typeof key != "object" || key instanceof Array){
10807                 config = {
10808                     key: key,
10809                     fn: fn,
10810                     scope: scope
10811                 };
10812             }else{
10813                 config = {
10814                     key : key.key,
10815                     shift : key.shift,
10816                     ctrl : key.ctrl,
10817                     alt : key.alt,
10818                     fn: fn,
10819                     scope: scope
10820                 };
10821             }
10822             return new Roo.KeyMap(this, config);
10823         },
10824
10825         /**
10826          * Creates a KeyMap for this element
10827          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10828          * @return {Roo.KeyMap} The KeyMap created
10829          */
10830         addKeyMap : function(config){
10831             return new Roo.KeyMap(this, config);
10832         },
10833
10834         /**
10835          * Returns true if this element is scrollable.
10836          * @return {Boolean}
10837          */
10838          isScrollable : function(){
10839             var dom = this.dom;
10840             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10841         },
10842
10843         /**
10844          * 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().
10845          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10846          * @param {Number} value The new scroll value
10847          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10848          * @return {Element} this
10849          */
10850
10851         scrollTo : function(side, value, animate){
10852             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10853             if(!animate || !A){
10854                 this.dom[prop] = value;
10855             }else{
10856                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10857                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10858             }
10859             return this;
10860         },
10861
10862         /**
10863          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10864          * within this element's scrollable range.
10865          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10866          * @param {Number} distance How far to scroll the element in pixels
10867          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10868          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10869          * was scrolled as far as it could go.
10870          */
10871          scroll : function(direction, distance, animate){
10872              if(!this.isScrollable()){
10873                  return;
10874              }
10875              var el = this.dom;
10876              var l = el.scrollLeft, t = el.scrollTop;
10877              var w = el.scrollWidth, h = el.scrollHeight;
10878              var cw = el.clientWidth, ch = el.clientHeight;
10879              direction = direction.toLowerCase();
10880              var scrolled = false;
10881              var a = this.preanim(arguments, 2);
10882              switch(direction){
10883                  case "l":
10884                  case "left":
10885                      if(w - l > cw){
10886                          var v = Math.min(l + distance, w-cw);
10887                          this.scrollTo("left", v, a);
10888                          scrolled = true;
10889                      }
10890                      break;
10891                 case "r":
10892                 case "right":
10893                      if(l > 0){
10894                          var v = Math.max(l - distance, 0);
10895                          this.scrollTo("left", v, a);
10896                          scrolled = true;
10897                      }
10898                      break;
10899                 case "t":
10900                 case "top":
10901                 case "up":
10902                      if(t > 0){
10903                          var v = Math.max(t - distance, 0);
10904                          this.scrollTo("top", v, a);
10905                          scrolled = true;
10906                      }
10907                      break;
10908                 case "b":
10909                 case "bottom":
10910                 case "down":
10911                      if(h - t > ch){
10912                          var v = Math.min(t + distance, h-ch);
10913                          this.scrollTo("top", v, a);
10914                          scrolled = true;
10915                      }
10916                      break;
10917              }
10918              return scrolled;
10919         },
10920
10921         /**
10922          * Translates the passed page coordinates into left/top css values for this element
10923          * @param {Number/Array} x The page x or an array containing [x, y]
10924          * @param {Number} y The page y
10925          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10926          */
10927         translatePoints : function(x, y){
10928             if(typeof x == 'object' || x instanceof Array){
10929                 y = x[1]; x = x[0];
10930             }
10931             var p = this.getStyle('position');
10932             var o = this.getXY();
10933
10934             var l = parseInt(this.getStyle('left'), 10);
10935             var t = parseInt(this.getStyle('top'), 10);
10936
10937             if(isNaN(l)){
10938                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10939             }
10940             if(isNaN(t)){
10941                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10942             }
10943
10944             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10945         },
10946
10947         /**
10948          * Returns the current scroll position of the element.
10949          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10950          */
10951         getScroll : function(){
10952             var d = this.dom, doc = document;
10953             if(d == doc || d == doc.body){
10954                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10955                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10956                 return {left: l, top: t};
10957             }else{
10958                 return {left: d.scrollLeft, top: d.scrollTop};
10959             }
10960         },
10961
10962         /**
10963          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10964          * are convert to standard 6 digit hex color.
10965          * @param {String} attr The css attribute
10966          * @param {String} defaultValue The default value to use when a valid color isn't found
10967          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10968          * YUI color anims.
10969          */
10970         getColor : function(attr, defaultValue, prefix){
10971             var v = this.getStyle(attr);
10972             if(!v || v == "transparent" || v == "inherit") {
10973                 return defaultValue;
10974             }
10975             var color = typeof prefix == "undefined" ? "#" : prefix;
10976             if(v.substr(0, 4) == "rgb("){
10977                 var rvs = v.slice(4, v.length -1).split(",");
10978                 for(var i = 0; i < 3; i++){
10979                     var h = parseInt(rvs[i]).toString(16);
10980                     if(h < 16){
10981                         h = "0" + h;
10982                     }
10983                     color += h;
10984                 }
10985             } else {
10986                 if(v.substr(0, 1) == "#"){
10987                     if(v.length == 4) {
10988                         for(var i = 1; i < 4; i++){
10989                             var c = v.charAt(i);
10990                             color +=  c + c;
10991                         }
10992                     }else if(v.length == 7){
10993                         color += v.substr(1);
10994                     }
10995                 }
10996             }
10997             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10998         },
10999
11000         /**
11001          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11002          * gradient background, rounded corners and a 4-way shadow.
11003          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11004          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11005          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11006          * @return {Roo.Element} this
11007          */
11008         boxWrap : function(cls){
11009             cls = cls || 'x-box';
11010             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11011             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11012             return el;
11013         },
11014
11015         /**
11016          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11017          * @param {String} namespace The namespace in which to look for the attribute
11018          * @param {String} name The attribute name
11019          * @return {String} The attribute value
11020          */
11021         getAttributeNS : Roo.isIE ? function(ns, name){
11022             var d = this.dom;
11023             var type = typeof d[ns+":"+name];
11024             if(type != 'undefined' && type != 'unknown'){
11025                 return d[ns+":"+name];
11026             }
11027             return d[name];
11028         } : function(ns, name){
11029             var d = this.dom;
11030             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11031         },
11032         
11033         
11034         /**
11035          * Sets or Returns the value the dom attribute value
11036          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11037          * @param {String} value (optional) The value to set the attribute to
11038          * @return {String} The attribute value
11039          */
11040         attr : function(name){
11041             if (arguments.length > 1) {
11042                 this.dom.setAttribute(name, arguments[1]);
11043                 return arguments[1];
11044             }
11045             if (typeof(name) == 'object') {
11046                 for(var i in name) {
11047                     this.attr(i, name[i]);
11048                 }
11049                 return name;
11050             }
11051             
11052             
11053             if (!this.dom.hasAttribute(name)) {
11054                 return undefined;
11055             }
11056             return this.dom.getAttribute(name);
11057         }
11058         
11059         
11060         
11061     };
11062
11063     var ep = El.prototype;
11064
11065     /**
11066      * Appends an event handler (Shorthand for addListener)
11067      * @param {String}   eventName     The type of event to append
11068      * @param {Function} fn        The method the event invokes
11069      * @param {Object} scope       (optional) The scope (this object) of the fn
11070      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11071      * @method
11072      */
11073     ep.on = ep.addListener;
11074         // backwards compat
11075     ep.mon = ep.addListener;
11076
11077     /**
11078      * Removes an event handler from this element (shorthand for removeListener)
11079      * @param {String} eventName the type of event to remove
11080      * @param {Function} fn the method the event invokes
11081      * @return {Roo.Element} this
11082      * @method
11083      */
11084     ep.un = ep.removeListener;
11085
11086     /**
11087      * true to automatically adjust width and height settings for box-model issues (default to true)
11088      */
11089     ep.autoBoxAdjust = true;
11090
11091     // private
11092     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11093
11094     // private
11095     El.addUnits = function(v, defaultUnit){
11096         if(v === "" || v == "auto"){
11097             return v;
11098         }
11099         if(v === undefined){
11100             return '';
11101         }
11102         if(typeof v == "number" || !El.unitPattern.test(v)){
11103             return v + (defaultUnit || 'px');
11104         }
11105         return v;
11106     };
11107
11108     // special markup used throughout Roo when box wrapping elements
11109     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>';
11110     /**
11111      * Visibility mode constant - Use visibility to hide element
11112      * @static
11113      * @type Number
11114      */
11115     El.VISIBILITY = 1;
11116     /**
11117      * Visibility mode constant - Use display to hide element
11118      * @static
11119      * @type Number
11120      */
11121     El.DISPLAY = 2;
11122
11123     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11124     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11125     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11126
11127
11128
11129     /**
11130      * @private
11131      */
11132     El.cache = {};
11133
11134     var docEl;
11135
11136     /**
11137      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11138      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11139      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11140      * @return {Element} The Element object
11141      * @static
11142      */
11143     El.get = function(el){
11144         var ex, elm, id;
11145         if(!el){ return null; }
11146         if(typeof el == "string"){ // element id
11147             if(!(elm = document.getElementById(el))){
11148                 return null;
11149             }
11150             if(ex = El.cache[el]){
11151                 ex.dom = elm;
11152             }else{
11153                 ex = El.cache[el] = new El(elm);
11154             }
11155             return ex;
11156         }else if(el.tagName){ // dom element
11157             if(!(id = el.id)){
11158                 id = Roo.id(el);
11159             }
11160             if(ex = El.cache[id]){
11161                 ex.dom = el;
11162             }else{
11163                 ex = El.cache[id] = new El(el);
11164             }
11165             return ex;
11166         }else if(el instanceof El){
11167             if(el != docEl){
11168                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11169                                                               // catch case where it hasn't been appended
11170                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11171             }
11172             return el;
11173         }else if(el.isComposite){
11174             return el;
11175         }else if(el instanceof Array){
11176             return El.select(el);
11177         }else if(el == document){
11178             // create a bogus element object representing the document object
11179             if(!docEl){
11180                 var f = function(){};
11181                 f.prototype = El.prototype;
11182                 docEl = new f();
11183                 docEl.dom = document;
11184             }
11185             return docEl;
11186         }
11187         return null;
11188     };
11189
11190     // private
11191     El.uncache = function(el){
11192         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11193             if(a[i]){
11194                 delete El.cache[a[i].id || a[i]];
11195             }
11196         }
11197     };
11198
11199     // private
11200     // Garbage collection - uncache elements/purge listeners on orphaned elements
11201     // so we don't hold a reference and cause the browser to retain them
11202     El.garbageCollect = function(){
11203         if(!Roo.enableGarbageCollector){
11204             clearInterval(El.collectorThread);
11205             return;
11206         }
11207         for(var eid in El.cache){
11208             var el = El.cache[eid], d = el.dom;
11209             // -------------------------------------------------------
11210             // Determining what is garbage:
11211             // -------------------------------------------------------
11212             // !d
11213             // dom node is null, definitely garbage
11214             // -------------------------------------------------------
11215             // !d.parentNode
11216             // no parentNode == direct orphan, definitely garbage
11217             // -------------------------------------------------------
11218             // !d.offsetParent && !document.getElementById(eid)
11219             // display none elements have no offsetParent so we will
11220             // also try to look it up by it's id. However, check
11221             // offsetParent first so we don't do unneeded lookups.
11222             // This enables collection of elements that are not orphans
11223             // directly, but somewhere up the line they have an orphan
11224             // parent.
11225             // -------------------------------------------------------
11226             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11227                 delete El.cache[eid];
11228                 if(d && Roo.enableListenerCollection){
11229                     E.purgeElement(d);
11230                 }
11231             }
11232         }
11233     }
11234     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11235
11236
11237     // dom is optional
11238     El.Flyweight = function(dom){
11239         this.dom = dom;
11240     };
11241     El.Flyweight.prototype = El.prototype;
11242
11243     El._flyweights = {};
11244     /**
11245      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11246      * the dom node can be overwritten by other code.
11247      * @param {String/HTMLElement} el The dom node or id
11248      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11249      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11250      * @static
11251      * @return {Element} The shared Element object
11252      */
11253     El.fly = function(el, named){
11254         named = named || '_global';
11255         el = Roo.getDom(el);
11256         if(!el){
11257             return null;
11258         }
11259         if(!El._flyweights[named]){
11260             El._flyweights[named] = new El.Flyweight();
11261         }
11262         El._flyweights[named].dom = el;
11263         return El._flyweights[named];
11264     };
11265
11266     /**
11267      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11268      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11269      * Shorthand of {@link Roo.Element#get}
11270      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11271      * @return {Element} The Element object
11272      * @member Roo
11273      * @method get
11274      */
11275     Roo.get = El.get;
11276     /**
11277      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11278      * the dom node can be overwritten by other code.
11279      * Shorthand of {@link Roo.Element#fly}
11280      * @param {String/HTMLElement} el The dom node or id
11281      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11282      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11283      * @static
11284      * @return {Element} The shared Element object
11285      * @member Roo
11286      * @method fly
11287      */
11288     Roo.fly = El.fly;
11289
11290     // speedy lookup for elements never to box adjust
11291     var noBoxAdjust = Roo.isStrict ? {
11292         select:1
11293     } : {
11294         input:1, select:1, textarea:1
11295     };
11296     if(Roo.isIE || Roo.isGecko){
11297         noBoxAdjust['button'] = 1;
11298     }
11299
11300
11301     Roo.EventManager.on(window, 'unload', function(){
11302         delete El.cache;
11303         delete El._flyweights;
11304     });
11305 })();
11306
11307
11308
11309
11310 if(Roo.DomQuery){
11311     Roo.Element.selectorFunction = Roo.DomQuery.select;
11312 }
11313
11314 Roo.Element.select = function(selector, unique, root){
11315     var els;
11316     if(typeof selector == "string"){
11317         els = Roo.Element.selectorFunction(selector, root);
11318     }else if(selector.length !== undefined){
11319         els = selector;
11320     }else{
11321         throw "Invalid selector";
11322     }
11323     if(unique === true){
11324         return new Roo.CompositeElement(els);
11325     }else{
11326         return new Roo.CompositeElementLite(els);
11327     }
11328 };
11329 /**
11330  * Selects elements based on the passed CSS selector to enable working on them as 1.
11331  * @param {String/Array} selector The CSS selector or an array of elements
11332  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11333  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11334  * @return {CompositeElementLite/CompositeElement}
11335  * @member Roo
11336  * @method select
11337  */
11338 Roo.select = Roo.Element.select;
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353 /*
11354  * Based on:
11355  * Ext JS Library 1.1.1
11356  * Copyright(c) 2006-2007, Ext JS, LLC.
11357  *
11358  * Originally Released Under LGPL - original licence link has changed is not relivant.
11359  *
11360  * Fork - LGPL
11361  * <script type="text/javascript">
11362  */
11363
11364
11365
11366 //Notifies Element that fx methods are available
11367 Roo.enableFx = true;
11368
11369 /**
11370  * @class Roo.Fx
11371  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11372  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11373  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11374  * Element effects to work.</p><br/>
11375  *
11376  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11377  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11378  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11379  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11380  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11381  * expected results and should be done with care.</p><br/>
11382  *
11383  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11384  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11385 <pre>
11386 Value  Description
11387 -----  -----------------------------
11388 tl     The top left corner
11389 t      The center of the top edge
11390 tr     The top right corner
11391 l      The center of the left edge
11392 r      The center of the right edge
11393 bl     The bottom left corner
11394 b      The center of the bottom edge
11395 br     The bottom right corner
11396 </pre>
11397  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11398  * below are common options that can be passed to any Fx method.</b>
11399  * @cfg {Function} callback A function called when the effect is finished
11400  * @cfg {Object} scope The scope of the effect function
11401  * @cfg {String} easing A valid Easing value for the effect
11402  * @cfg {String} afterCls A css class to apply after the effect
11403  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11404  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11405  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11406  * effects that end with the element being visually hidden, ignored otherwise)
11407  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11408  * a function which returns such a specification that will be applied to the Element after the effect finishes
11409  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11410  * @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
11411  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11412  */
11413 Roo.Fx = {
11414         /**
11415          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11416          * origin for the slide effect.  This function automatically handles wrapping the element with
11417          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11418          * Usage:
11419          *<pre><code>
11420 // default: slide the element in from the top
11421 el.slideIn();
11422
11423 // custom: slide the element in from the right with a 2-second duration
11424 el.slideIn('r', { duration: 2 });
11425
11426 // common config options shown with default values
11427 el.slideIn('t', {
11428     easing: 'easeOut',
11429     duration: .5
11430 });
11431 </code></pre>
11432          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11433          * @param {Object} options (optional) Object literal with any of the Fx config options
11434          * @return {Roo.Element} The Element
11435          */
11436     slideIn : function(anchor, o){
11437         var el = this.getFxEl();
11438         o = o || {};
11439
11440         el.queueFx(o, function(){
11441
11442             anchor = anchor || "t";
11443
11444             // fix display to visibility
11445             this.fixDisplay();
11446
11447             // restore values after effect
11448             var r = this.getFxRestore();
11449             var b = this.getBox();
11450             // fixed size for slide
11451             this.setSize(b);
11452
11453             // wrap if needed
11454             var wrap = this.fxWrap(r.pos, o, "hidden");
11455
11456             var st = this.dom.style;
11457             st.visibility = "visible";
11458             st.position = "absolute";
11459
11460             // clear out temp styles after slide and unwrap
11461             var after = function(){
11462                 el.fxUnwrap(wrap, r.pos, o);
11463                 st.width = r.width;
11464                 st.height = r.height;
11465                 el.afterFx(o);
11466             };
11467             // time to calc the positions
11468             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11469
11470             switch(anchor.toLowerCase()){
11471                 case "t":
11472                     wrap.setSize(b.width, 0);
11473                     st.left = st.bottom = "0";
11474                     a = {height: bh};
11475                 break;
11476                 case "l":
11477                     wrap.setSize(0, b.height);
11478                     st.right = st.top = "0";
11479                     a = {width: bw};
11480                 break;
11481                 case "r":
11482                     wrap.setSize(0, b.height);
11483                     wrap.setX(b.right);
11484                     st.left = st.top = "0";
11485                     a = {width: bw, points: pt};
11486                 break;
11487                 case "b":
11488                     wrap.setSize(b.width, 0);
11489                     wrap.setY(b.bottom);
11490                     st.left = st.top = "0";
11491                     a = {height: bh, points: pt};
11492                 break;
11493                 case "tl":
11494                     wrap.setSize(0, 0);
11495                     st.right = st.bottom = "0";
11496                     a = {width: bw, height: bh};
11497                 break;
11498                 case "bl":
11499                     wrap.setSize(0, 0);
11500                     wrap.setY(b.y+b.height);
11501                     st.right = st.top = "0";
11502                     a = {width: bw, height: bh, points: pt};
11503                 break;
11504                 case "br":
11505                     wrap.setSize(0, 0);
11506                     wrap.setXY([b.right, b.bottom]);
11507                     st.left = st.top = "0";
11508                     a = {width: bw, height: bh, points: pt};
11509                 break;
11510                 case "tr":
11511                     wrap.setSize(0, 0);
11512                     wrap.setX(b.x+b.width);
11513                     st.left = st.bottom = "0";
11514                     a = {width: bw, height: bh, points: pt};
11515                 break;
11516             }
11517             this.dom.style.visibility = "visible";
11518             wrap.show();
11519
11520             arguments.callee.anim = wrap.fxanim(a,
11521                 o,
11522                 'motion',
11523                 .5,
11524                 'easeOut', after);
11525         });
11526         return this;
11527     },
11528     
11529         /**
11530          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11531          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11532          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11533          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11534          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11535          * Usage:
11536          *<pre><code>
11537 // default: slide the element out to the top
11538 el.slideOut();
11539
11540 // custom: slide the element out to the right with a 2-second duration
11541 el.slideOut('r', { duration: 2 });
11542
11543 // common config options shown with default values
11544 el.slideOut('t', {
11545     easing: 'easeOut',
11546     duration: .5,
11547     remove: false,
11548     useDisplay: false
11549 });
11550 </code></pre>
11551          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11552          * @param {Object} options (optional) Object literal with any of the Fx config options
11553          * @return {Roo.Element} The Element
11554          */
11555     slideOut : function(anchor, o){
11556         var el = this.getFxEl();
11557         o = o || {};
11558
11559         el.queueFx(o, function(){
11560
11561             anchor = anchor || "t";
11562
11563             // restore values after effect
11564             var r = this.getFxRestore();
11565             
11566             var b = this.getBox();
11567             // fixed size for slide
11568             this.setSize(b);
11569
11570             // wrap if needed
11571             var wrap = this.fxWrap(r.pos, o, "visible");
11572
11573             var st = this.dom.style;
11574             st.visibility = "visible";
11575             st.position = "absolute";
11576
11577             wrap.setSize(b);
11578
11579             var after = function(){
11580                 if(o.useDisplay){
11581                     el.setDisplayed(false);
11582                 }else{
11583                     el.hide();
11584                 }
11585
11586                 el.fxUnwrap(wrap, r.pos, o);
11587
11588                 st.width = r.width;
11589                 st.height = r.height;
11590
11591                 el.afterFx(o);
11592             };
11593
11594             var a, zero = {to: 0};
11595             switch(anchor.toLowerCase()){
11596                 case "t":
11597                     st.left = st.bottom = "0";
11598                     a = {height: zero};
11599                 break;
11600                 case "l":
11601                     st.right = st.top = "0";
11602                     a = {width: zero};
11603                 break;
11604                 case "r":
11605                     st.left = st.top = "0";
11606                     a = {width: zero, points: {to:[b.right, b.y]}};
11607                 break;
11608                 case "b":
11609                     st.left = st.top = "0";
11610                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11611                 break;
11612                 case "tl":
11613                     st.right = st.bottom = "0";
11614                     a = {width: zero, height: zero};
11615                 break;
11616                 case "bl":
11617                     st.right = st.top = "0";
11618                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11619                 break;
11620                 case "br":
11621                     st.left = st.top = "0";
11622                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11623                 break;
11624                 case "tr":
11625                     st.left = st.bottom = "0";
11626                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11627                 break;
11628             }
11629
11630             arguments.callee.anim = wrap.fxanim(a,
11631                 o,
11632                 'motion',
11633                 .5,
11634                 "easeOut", after);
11635         });
11636         return this;
11637     },
11638
11639         /**
11640          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11641          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11642          * The element must be removed from the DOM using the 'remove' config option if desired.
11643          * Usage:
11644          *<pre><code>
11645 // default
11646 el.puff();
11647
11648 // common config options shown with default values
11649 el.puff({
11650     easing: 'easeOut',
11651     duration: .5,
11652     remove: false,
11653     useDisplay: false
11654 });
11655 </code></pre>
11656          * @param {Object} options (optional) Object literal with any of the Fx config options
11657          * @return {Roo.Element} The Element
11658          */
11659     puff : function(o){
11660         var el = this.getFxEl();
11661         o = o || {};
11662
11663         el.queueFx(o, function(){
11664             this.clearOpacity();
11665             this.show();
11666
11667             // restore values after effect
11668             var r = this.getFxRestore();
11669             var st = this.dom.style;
11670
11671             var after = function(){
11672                 if(o.useDisplay){
11673                     el.setDisplayed(false);
11674                 }else{
11675                     el.hide();
11676                 }
11677
11678                 el.clearOpacity();
11679
11680                 el.setPositioning(r.pos);
11681                 st.width = r.width;
11682                 st.height = r.height;
11683                 st.fontSize = '';
11684                 el.afterFx(o);
11685             };
11686
11687             var width = this.getWidth();
11688             var height = this.getHeight();
11689
11690             arguments.callee.anim = this.fxanim({
11691                     width : {to: this.adjustWidth(width * 2)},
11692                     height : {to: this.adjustHeight(height * 2)},
11693                     points : {by: [-(width * .5), -(height * .5)]},
11694                     opacity : {to: 0},
11695                     fontSize: {to:200, unit: "%"}
11696                 },
11697                 o,
11698                 'motion',
11699                 .5,
11700                 "easeOut", after);
11701         });
11702         return this;
11703     },
11704
11705         /**
11706          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11707          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11708          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11709          * Usage:
11710          *<pre><code>
11711 // default
11712 el.switchOff();
11713
11714 // all config options shown with default values
11715 el.switchOff({
11716     easing: 'easeIn',
11717     duration: .3,
11718     remove: false,
11719     useDisplay: false
11720 });
11721 </code></pre>
11722          * @param {Object} options (optional) Object literal with any of the Fx config options
11723          * @return {Roo.Element} The Element
11724          */
11725     switchOff : function(o){
11726         var el = this.getFxEl();
11727         o = o || {};
11728
11729         el.queueFx(o, function(){
11730             this.clearOpacity();
11731             this.clip();
11732
11733             // restore values after effect
11734             var r = this.getFxRestore();
11735             var st = this.dom.style;
11736
11737             var after = function(){
11738                 if(o.useDisplay){
11739                     el.setDisplayed(false);
11740                 }else{
11741                     el.hide();
11742                 }
11743
11744                 el.clearOpacity();
11745                 el.setPositioning(r.pos);
11746                 st.width = r.width;
11747                 st.height = r.height;
11748
11749                 el.afterFx(o);
11750             };
11751
11752             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11753                 this.clearOpacity();
11754                 (function(){
11755                     this.fxanim({
11756                         height:{to:1},
11757                         points:{by:[0, this.getHeight() * .5]}
11758                     }, o, 'motion', 0.3, 'easeIn', after);
11759                 }).defer(100, this);
11760             });
11761         });
11762         return this;
11763     },
11764
11765     /**
11766      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11767      * changed using the "attr" config option) and then fading back to the original color. If no original
11768      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11769      * Usage:
11770 <pre><code>
11771 // default: highlight background to yellow
11772 el.highlight();
11773
11774 // custom: highlight foreground text to blue for 2 seconds
11775 el.highlight("0000ff", { attr: 'color', duration: 2 });
11776
11777 // common config options shown with default values
11778 el.highlight("ffff9c", {
11779     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11780     endColor: (current color) or "ffffff",
11781     easing: 'easeIn',
11782     duration: 1
11783 });
11784 </code></pre>
11785      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11786      * @param {Object} options (optional) Object literal with any of the Fx config options
11787      * @return {Roo.Element} The Element
11788      */ 
11789     highlight : function(color, o){
11790         var el = this.getFxEl();
11791         o = o || {};
11792
11793         el.queueFx(o, function(){
11794             color = color || "ffff9c";
11795             attr = o.attr || "backgroundColor";
11796
11797             this.clearOpacity();
11798             this.show();
11799
11800             var origColor = this.getColor(attr);
11801             var restoreColor = this.dom.style[attr];
11802             endColor = (o.endColor || origColor) || "ffffff";
11803
11804             var after = function(){
11805                 el.dom.style[attr] = restoreColor;
11806                 el.afterFx(o);
11807             };
11808
11809             var a = {};
11810             a[attr] = {from: color, to: endColor};
11811             arguments.callee.anim = this.fxanim(a,
11812                 o,
11813                 'color',
11814                 1,
11815                 'easeIn', after);
11816         });
11817         return this;
11818     },
11819
11820    /**
11821     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11822     * Usage:
11823 <pre><code>
11824 // default: a single light blue ripple
11825 el.frame();
11826
11827 // custom: 3 red ripples lasting 3 seconds total
11828 el.frame("ff0000", 3, { duration: 3 });
11829
11830 // common config options shown with default values
11831 el.frame("C3DAF9", 1, {
11832     duration: 1 //duration of entire animation (not each individual ripple)
11833     // Note: Easing is not configurable and will be ignored if included
11834 });
11835 </code></pre>
11836     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11837     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11838     * @param {Object} options (optional) Object literal with any of the Fx config options
11839     * @return {Roo.Element} The Element
11840     */
11841     frame : function(color, count, o){
11842         var el = this.getFxEl();
11843         o = o || {};
11844
11845         el.queueFx(o, function(){
11846             color = color || "#C3DAF9";
11847             if(color.length == 6){
11848                 color = "#" + color;
11849             }
11850             count = count || 1;
11851             duration = o.duration || 1;
11852             this.show();
11853
11854             var b = this.getBox();
11855             var animFn = function(){
11856                 var proxy = this.createProxy({
11857
11858                      style:{
11859                         visbility:"hidden",
11860                         position:"absolute",
11861                         "z-index":"35000", // yee haw
11862                         border:"0px solid " + color
11863                      }
11864                   });
11865                 var scale = Roo.isBorderBox ? 2 : 1;
11866                 proxy.animate({
11867                     top:{from:b.y, to:b.y - 20},
11868                     left:{from:b.x, to:b.x - 20},
11869                     borderWidth:{from:0, to:10},
11870                     opacity:{from:1, to:0},
11871                     height:{from:b.height, to:(b.height + (20*scale))},
11872                     width:{from:b.width, to:(b.width + (20*scale))}
11873                 }, duration, function(){
11874                     proxy.remove();
11875                 });
11876                 if(--count > 0){
11877                      animFn.defer((duration/2)*1000, this);
11878                 }else{
11879                     el.afterFx(o);
11880                 }
11881             };
11882             animFn.call(this);
11883         });
11884         return this;
11885     },
11886
11887    /**
11888     * Creates a pause before any subsequent queued effects begin.  If there are
11889     * no effects queued after the pause it will have no effect.
11890     * Usage:
11891 <pre><code>
11892 el.pause(1);
11893 </code></pre>
11894     * @param {Number} seconds The length of time to pause (in seconds)
11895     * @return {Roo.Element} The Element
11896     */
11897     pause : function(seconds){
11898         var el = this.getFxEl();
11899         var o = {};
11900
11901         el.queueFx(o, function(){
11902             setTimeout(function(){
11903                 el.afterFx(o);
11904             }, seconds * 1000);
11905         });
11906         return this;
11907     },
11908
11909    /**
11910     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11911     * using the "endOpacity" config option.
11912     * Usage:
11913 <pre><code>
11914 // default: fade in from opacity 0 to 100%
11915 el.fadeIn();
11916
11917 // custom: fade in from opacity 0 to 75% over 2 seconds
11918 el.fadeIn({ endOpacity: .75, duration: 2});
11919
11920 // common config options shown with default values
11921 el.fadeIn({
11922     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11923     easing: 'easeOut',
11924     duration: .5
11925 });
11926 </code></pre>
11927     * @param {Object} options (optional) Object literal with any of the Fx config options
11928     * @return {Roo.Element} The Element
11929     */
11930     fadeIn : function(o){
11931         var el = this.getFxEl();
11932         o = o || {};
11933         el.queueFx(o, function(){
11934             this.setOpacity(0);
11935             this.fixDisplay();
11936             this.dom.style.visibility = 'visible';
11937             var to = o.endOpacity || 1;
11938             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11939                 o, null, .5, "easeOut", function(){
11940                 if(to == 1){
11941                     this.clearOpacity();
11942                 }
11943                 el.afterFx(o);
11944             });
11945         });
11946         return this;
11947     },
11948
11949    /**
11950     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11951     * using the "endOpacity" config option.
11952     * Usage:
11953 <pre><code>
11954 // default: fade out from the element's current opacity to 0
11955 el.fadeOut();
11956
11957 // custom: fade out from the element's current opacity to 25% over 2 seconds
11958 el.fadeOut({ endOpacity: .25, duration: 2});
11959
11960 // common config options shown with default values
11961 el.fadeOut({
11962     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11963     easing: 'easeOut',
11964     duration: .5
11965     remove: false,
11966     useDisplay: false
11967 });
11968 </code></pre>
11969     * @param {Object} options (optional) Object literal with any of the Fx config options
11970     * @return {Roo.Element} The Element
11971     */
11972     fadeOut : function(o){
11973         var el = this.getFxEl();
11974         o = o || {};
11975         el.queueFx(o, function(){
11976             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11977                 o, null, .5, "easeOut", function(){
11978                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11979                      this.dom.style.display = "none";
11980                 }else{
11981                      this.dom.style.visibility = "hidden";
11982                 }
11983                 this.clearOpacity();
11984                 el.afterFx(o);
11985             });
11986         });
11987         return this;
11988     },
11989
11990    /**
11991     * Animates the transition of an element's dimensions from a starting height/width
11992     * to an ending height/width.
11993     * Usage:
11994 <pre><code>
11995 // change height and width to 100x100 pixels
11996 el.scale(100, 100);
11997
11998 // common config options shown with default values.  The height and width will default to
11999 // the element's existing values if passed as null.
12000 el.scale(
12001     [element's width],
12002     [element's height], {
12003     easing: 'easeOut',
12004     duration: .35
12005 });
12006 </code></pre>
12007     * @param {Number} width  The new width (pass undefined to keep the original width)
12008     * @param {Number} height  The new height (pass undefined to keep the original height)
12009     * @param {Object} options (optional) Object literal with any of the Fx config options
12010     * @return {Roo.Element} The Element
12011     */
12012     scale : function(w, h, o){
12013         this.shift(Roo.apply({}, o, {
12014             width: w,
12015             height: h
12016         }));
12017         return this;
12018     },
12019
12020    /**
12021     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12022     * Any of these properties not specified in the config object will not be changed.  This effect 
12023     * requires that at least one new dimension, position or opacity setting must be passed in on
12024     * the config object in order for the function to have any effect.
12025     * Usage:
12026 <pre><code>
12027 // slide the element horizontally to x position 200 while changing the height and opacity
12028 el.shift({ x: 200, height: 50, opacity: .8 });
12029
12030 // common config options shown with default values.
12031 el.shift({
12032     width: [element's width],
12033     height: [element's height],
12034     x: [element's x position],
12035     y: [element's y position],
12036     opacity: [element's opacity],
12037     easing: 'easeOut',
12038     duration: .35
12039 });
12040 </code></pre>
12041     * @param {Object} options  Object literal with any of the Fx config options
12042     * @return {Roo.Element} The Element
12043     */
12044     shift : function(o){
12045         var el = this.getFxEl();
12046         o = o || {};
12047         el.queueFx(o, function(){
12048             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12049             if(w !== undefined){
12050                 a.width = {to: this.adjustWidth(w)};
12051             }
12052             if(h !== undefined){
12053                 a.height = {to: this.adjustHeight(h)};
12054             }
12055             if(x !== undefined || y !== undefined){
12056                 a.points = {to: [
12057                     x !== undefined ? x : this.getX(),
12058                     y !== undefined ? y : this.getY()
12059                 ]};
12060             }
12061             if(op !== undefined){
12062                 a.opacity = {to: op};
12063             }
12064             if(o.xy !== undefined){
12065                 a.points = {to: o.xy};
12066             }
12067             arguments.callee.anim = this.fxanim(a,
12068                 o, 'motion', .35, "easeOut", function(){
12069                 el.afterFx(o);
12070             });
12071         });
12072         return this;
12073     },
12074
12075         /**
12076          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12077          * ending point of the effect.
12078          * Usage:
12079          *<pre><code>
12080 // default: slide the element downward while fading out
12081 el.ghost();
12082
12083 // custom: slide the element out to the right with a 2-second duration
12084 el.ghost('r', { duration: 2 });
12085
12086 // common config options shown with default values
12087 el.ghost('b', {
12088     easing: 'easeOut',
12089     duration: .5
12090     remove: false,
12091     useDisplay: false
12092 });
12093 </code></pre>
12094          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12095          * @param {Object} options (optional) Object literal with any of the Fx config options
12096          * @return {Roo.Element} The Element
12097          */
12098     ghost : function(anchor, o){
12099         var el = this.getFxEl();
12100         o = o || {};
12101
12102         el.queueFx(o, function(){
12103             anchor = anchor || "b";
12104
12105             // restore values after effect
12106             var r = this.getFxRestore();
12107             var w = this.getWidth(),
12108                 h = this.getHeight();
12109
12110             var st = this.dom.style;
12111
12112             var after = function(){
12113                 if(o.useDisplay){
12114                     el.setDisplayed(false);
12115                 }else{
12116                     el.hide();
12117                 }
12118
12119                 el.clearOpacity();
12120                 el.setPositioning(r.pos);
12121                 st.width = r.width;
12122                 st.height = r.height;
12123
12124                 el.afterFx(o);
12125             };
12126
12127             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12128             switch(anchor.toLowerCase()){
12129                 case "t":
12130                     pt.by = [0, -h];
12131                 break;
12132                 case "l":
12133                     pt.by = [-w, 0];
12134                 break;
12135                 case "r":
12136                     pt.by = [w, 0];
12137                 break;
12138                 case "b":
12139                     pt.by = [0, h];
12140                 break;
12141                 case "tl":
12142                     pt.by = [-w, -h];
12143                 break;
12144                 case "bl":
12145                     pt.by = [-w, h];
12146                 break;
12147                 case "br":
12148                     pt.by = [w, h];
12149                 break;
12150                 case "tr":
12151                     pt.by = [w, -h];
12152                 break;
12153             }
12154
12155             arguments.callee.anim = this.fxanim(a,
12156                 o,
12157                 'motion',
12158                 .5,
12159                 "easeOut", after);
12160         });
12161         return this;
12162     },
12163
12164         /**
12165          * Ensures that all effects queued after syncFx is called on the element are
12166          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12167          * @return {Roo.Element} The Element
12168          */
12169     syncFx : function(){
12170         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12171             block : false,
12172             concurrent : true,
12173             stopFx : false
12174         });
12175         return this;
12176     },
12177
12178         /**
12179          * Ensures that all effects queued after sequenceFx is called on the element are
12180          * run in sequence.  This is the opposite of {@link #syncFx}.
12181          * @return {Roo.Element} The Element
12182          */
12183     sequenceFx : function(){
12184         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12185             block : false,
12186             concurrent : false,
12187             stopFx : false
12188         });
12189         return this;
12190     },
12191
12192         /* @private */
12193     nextFx : function(){
12194         var ef = this.fxQueue[0];
12195         if(ef){
12196             ef.call(this);
12197         }
12198     },
12199
12200         /**
12201          * Returns true if the element has any effects actively running or queued, else returns false.
12202          * @return {Boolean} True if element has active effects, else false
12203          */
12204     hasActiveFx : function(){
12205         return this.fxQueue && this.fxQueue[0];
12206     },
12207
12208         /**
12209          * Stops any running effects and clears the element's internal effects queue if it contains
12210          * any additional effects that haven't started yet.
12211          * @return {Roo.Element} The Element
12212          */
12213     stopFx : function(){
12214         if(this.hasActiveFx()){
12215             var cur = this.fxQueue[0];
12216             if(cur && cur.anim && cur.anim.isAnimated()){
12217                 this.fxQueue = [cur]; // clear out others
12218                 cur.anim.stop(true);
12219             }
12220         }
12221         return this;
12222     },
12223
12224         /* @private */
12225     beforeFx : function(o){
12226         if(this.hasActiveFx() && !o.concurrent){
12227            if(o.stopFx){
12228                this.stopFx();
12229                return true;
12230            }
12231            return false;
12232         }
12233         return true;
12234     },
12235
12236         /**
12237          * Returns true if the element is currently blocking so that no other effect can be queued
12238          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12239          * used to ensure that an effect initiated by a user action runs to completion prior to the
12240          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12241          * @return {Boolean} True if blocking, else false
12242          */
12243     hasFxBlock : function(){
12244         var q = this.fxQueue;
12245         return q && q[0] && q[0].block;
12246     },
12247
12248         /* @private */
12249     queueFx : function(o, fn){
12250         if(!this.fxQueue){
12251             this.fxQueue = [];
12252         }
12253         if(!this.hasFxBlock()){
12254             Roo.applyIf(o, this.fxDefaults);
12255             if(!o.concurrent){
12256                 var run = this.beforeFx(o);
12257                 fn.block = o.block;
12258                 this.fxQueue.push(fn);
12259                 if(run){
12260                     this.nextFx();
12261                 }
12262             }else{
12263                 fn.call(this);
12264             }
12265         }
12266         return this;
12267     },
12268
12269         /* @private */
12270     fxWrap : function(pos, o, vis){
12271         var wrap;
12272         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12273             var wrapXY;
12274             if(o.fixPosition){
12275                 wrapXY = this.getXY();
12276             }
12277             var div = document.createElement("div");
12278             div.style.visibility = vis;
12279             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12280             wrap.setPositioning(pos);
12281             if(wrap.getStyle("position") == "static"){
12282                 wrap.position("relative");
12283             }
12284             this.clearPositioning('auto');
12285             wrap.clip();
12286             wrap.dom.appendChild(this.dom);
12287             if(wrapXY){
12288                 wrap.setXY(wrapXY);
12289             }
12290         }
12291         return wrap;
12292     },
12293
12294         /* @private */
12295     fxUnwrap : function(wrap, pos, o){
12296         this.clearPositioning();
12297         this.setPositioning(pos);
12298         if(!o.wrap){
12299             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12300             wrap.remove();
12301         }
12302     },
12303
12304         /* @private */
12305     getFxRestore : function(){
12306         var st = this.dom.style;
12307         return {pos: this.getPositioning(), width: st.width, height : st.height};
12308     },
12309
12310         /* @private */
12311     afterFx : function(o){
12312         if(o.afterStyle){
12313             this.applyStyles(o.afterStyle);
12314         }
12315         if(o.afterCls){
12316             this.addClass(o.afterCls);
12317         }
12318         if(o.remove === true){
12319             this.remove();
12320         }
12321         Roo.callback(o.callback, o.scope, [this]);
12322         if(!o.concurrent){
12323             this.fxQueue.shift();
12324             this.nextFx();
12325         }
12326     },
12327
12328         /* @private */
12329     getFxEl : function(){ // support for composite element fx
12330         return Roo.get(this.dom);
12331     },
12332
12333         /* @private */
12334     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12335         animType = animType || 'run';
12336         opt = opt || {};
12337         var anim = Roo.lib.Anim[animType](
12338             this.dom, args,
12339             (opt.duration || defaultDur) || .35,
12340             (opt.easing || defaultEase) || 'easeOut',
12341             function(){
12342                 Roo.callback(cb, this);
12343             },
12344             this
12345         );
12346         opt.anim = anim;
12347         return anim;
12348     }
12349 };
12350
12351 // backwords compat
12352 Roo.Fx.resize = Roo.Fx.scale;
12353
12354 //When included, Roo.Fx is automatically applied to Element so that all basic
12355 //effects are available directly via the Element API
12356 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12357  * Based on:
12358  * Ext JS Library 1.1.1
12359  * Copyright(c) 2006-2007, Ext JS, LLC.
12360  *
12361  * Originally Released Under LGPL - original licence link has changed is not relivant.
12362  *
12363  * Fork - LGPL
12364  * <script type="text/javascript">
12365  */
12366
12367
12368 /**
12369  * @class Roo.CompositeElement
12370  * Standard composite class. Creates a Roo.Element for every element in the collection.
12371  * <br><br>
12372  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12373  * actions will be performed on all the elements in this collection.</b>
12374  * <br><br>
12375  * All methods return <i>this</i> and can be chained.
12376  <pre><code>
12377  var els = Roo.select("#some-el div.some-class", true);
12378  // or select directly from an existing element
12379  var el = Roo.get('some-el');
12380  el.select('div.some-class', true);
12381
12382  els.setWidth(100); // all elements become 100 width
12383  els.hide(true); // all elements fade out and hide
12384  // or
12385  els.setWidth(100).hide(true);
12386  </code></pre>
12387  */
12388 Roo.CompositeElement = function(els){
12389     this.elements = [];
12390     this.addElements(els);
12391 };
12392 Roo.CompositeElement.prototype = {
12393     isComposite: true,
12394     addElements : function(els){
12395         if(!els) {
12396             return this;
12397         }
12398         if(typeof els == "string"){
12399             els = Roo.Element.selectorFunction(els);
12400         }
12401         var yels = this.elements;
12402         var index = yels.length-1;
12403         for(var i = 0, len = els.length; i < len; i++) {
12404                 yels[++index] = Roo.get(els[i]);
12405         }
12406         return this;
12407     },
12408
12409     /**
12410     * Clears this composite and adds the elements returned by the passed selector.
12411     * @param {String/Array} els A string CSS selector, an array of elements or an element
12412     * @return {CompositeElement} this
12413     */
12414     fill : function(els){
12415         this.elements = [];
12416         this.add(els);
12417         return this;
12418     },
12419
12420     /**
12421     * Filters this composite to only elements that match the passed selector.
12422     * @param {String} selector A string CSS selector
12423     * @param {Boolean} inverse return inverse filter (not matches)
12424     * @return {CompositeElement} this
12425     */
12426     filter : function(selector, inverse){
12427         var els = [];
12428         inverse = inverse || false;
12429         this.each(function(el){
12430             var match = inverse ? !el.is(selector) : el.is(selector);
12431             if(match){
12432                 els[els.length] = el.dom;
12433             }
12434         });
12435         this.fill(els);
12436         return this;
12437     },
12438
12439     invoke : function(fn, args){
12440         var els = this.elements;
12441         for(var i = 0, len = els.length; i < len; i++) {
12442                 Roo.Element.prototype[fn].apply(els[i], args);
12443         }
12444         return this;
12445     },
12446     /**
12447     * Adds elements to this composite.
12448     * @param {String/Array} els A string CSS selector, an array of elements or an element
12449     * @return {CompositeElement} this
12450     */
12451     add : function(els){
12452         if(typeof els == "string"){
12453             this.addElements(Roo.Element.selectorFunction(els));
12454         }else if(els.length !== undefined){
12455             this.addElements(els);
12456         }else{
12457             this.addElements([els]);
12458         }
12459         return this;
12460     },
12461     /**
12462     * Calls the passed function passing (el, this, index) for each element in this composite.
12463     * @param {Function} fn The function to call
12464     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12465     * @return {CompositeElement} this
12466     */
12467     each : function(fn, scope){
12468         var els = this.elements;
12469         for(var i = 0, len = els.length; i < len; i++){
12470             if(fn.call(scope || els[i], els[i], this, i) === false) {
12471                 break;
12472             }
12473         }
12474         return this;
12475     },
12476
12477     /**
12478      * Returns the Element object at the specified index
12479      * @param {Number} index
12480      * @return {Roo.Element}
12481      */
12482     item : function(index){
12483         return this.elements[index] || null;
12484     },
12485
12486     /**
12487      * Returns the first Element
12488      * @return {Roo.Element}
12489      */
12490     first : function(){
12491         return this.item(0);
12492     },
12493
12494     /**
12495      * Returns the last Element
12496      * @return {Roo.Element}
12497      */
12498     last : function(){
12499         return this.item(this.elements.length-1);
12500     },
12501
12502     /**
12503      * Returns the number of elements in this composite
12504      * @return Number
12505      */
12506     getCount : function(){
12507         return this.elements.length;
12508     },
12509
12510     /**
12511      * Returns true if this composite contains the passed element
12512      * @return Boolean
12513      */
12514     contains : function(el){
12515         return this.indexOf(el) !== -1;
12516     },
12517
12518     /**
12519      * Returns true if this composite contains the passed element
12520      * @return Boolean
12521      */
12522     indexOf : function(el){
12523         return this.elements.indexOf(Roo.get(el));
12524     },
12525
12526
12527     /**
12528     * Removes the specified element(s).
12529     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12530     * or an array of any of those.
12531     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12532     * @return {CompositeElement} this
12533     */
12534     removeElement : function(el, removeDom){
12535         if(el instanceof Array){
12536             for(var i = 0, len = el.length; i < len; i++){
12537                 this.removeElement(el[i]);
12538             }
12539             return this;
12540         }
12541         var index = typeof el == 'number' ? el : this.indexOf(el);
12542         if(index !== -1){
12543             if(removeDom){
12544                 var d = this.elements[index];
12545                 if(d.dom){
12546                     d.remove();
12547                 }else{
12548                     d.parentNode.removeChild(d);
12549                 }
12550             }
12551             this.elements.splice(index, 1);
12552         }
12553         return this;
12554     },
12555
12556     /**
12557     * Replaces the specified element with the passed element.
12558     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12559     * to replace.
12560     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12561     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12562     * @return {CompositeElement} this
12563     */
12564     replaceElement : function(el, replacement, domReplace){
12565         var index = typeof el == 'number' ? el : this.indexOf(el);
12566         if(index !== -1){
12567             if(domReplace){
12568                 this.elements[index].replaceWith(replacement);
12569             }else{
12570                 this.elements.splice(index, 1, Roo.get(replacement))
12571             }
12572         }
12573         return this;
12574     },
12575
12576     /**
12577      * Removes all elements.
12578      */
12579     clear : function(){
12580         this.elements = [];
12581     }
12582 };
12583 (function(){
12584     Roo.CompositeElement.createCall = function(proto, fnName){
12585         if(!proto[fnName]){
12586             proto[fnName] = function(){
12587                 return this.invoke(fnName, arguments);
12588             };
12589         }
12590     };
12591     for(var fnName in Roo.Element.prototype){
12592         if(typeof Roo.Element.prototype[fnName] == "function"){
12593             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12594         }
12595     };
12596 })();
12597 /*
12598  * Based on:
12599  * Ext JS Library 1.1.1
12600  * Copyright(c) 2006-2007, Ext JS, LLC.
12601  *
12602  * Originally Released Under LGPL - original licence link has changed is not relivant.
12603  *
12604  * Fork - LGPL
12605  * <script type="text/javascript">
12606  */
12607
12608 /**
12609  * @class Roo.CompositeElementLite
12610  * @extends Roo.CompositeElement
12611  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12612  <pre><code>
12613  var els = Roo.select("#some-el div.some-class");
12614  // or select directly from an existing element
12615  var el = Roo.get('some-el');
12616  el.select('div.some-class');
12617
12618  els.setWidth(100); // all elements become 100 width
12619  els.hide(true); // all elements fade out and hide
12620  // or
12621  els.setWidth(100).hide(true);
12622  </code></pre><br><br>
12623  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12624  * actions will be performed on all the elements in this collection.</b>
12625  */
12626 Roo.CompositeElementLite = function(els){
12627     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12628     this.el = new Roo.Element.Flyweight();
12629 };
12630 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12631     addElements : function(els){
12632         if(els){
12633             if(els instanceof Array){
12634                 this.elements = this.elements.concat(els);
12635             }else{
12636                 var yels = this.elements;
12637                 var index = yels.length-1;
12638                 for(var i = 0, len = els.length; i < len; i++) {
12639                     yels[++index] = els[i];
12640                 }
12641             }
12642         }
12643         return this;
12644     },
12645     invoke : function(fn, args){
12646         var els = this.elements;
12647         var el = this.el;
12648         for(var i = 0, len = els.length; i < len; i++) {
12649             el.dom = els[i];
12650                 Roo.Element.prototype[fn].apply(el, args);
12651         }
12652         return this;
12653     },
12654     /**
12655      * Returns a flyweight Element of the dom element object at the specified index
12656      * @param {Number} index
12657      * @return {Roo.Element}
12658      */
12659     item : function(index){
12660         if(!this.elements[index]){
12661             return null;
12662         }
12663         this.el.dom = this.elements[index];
12664         return this.el;
12665     },
12666
12667     // fixes scope with flyweight
12668     addListener : function(eventName, handler, scope, opt){
12669         var els = this.elements;
12670         for(var i = 0, len = els.length; i < len; i++) {
12671             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12672         }
12673         return this;
12674     },
12675
12676     /**
12677     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12678     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12679     * a reference to the dom node, use el.dom.</b>
12680     * @param {Function} fn The function to call
12681     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12682     * @return {CompositeElement} this
12683     */
12684     each : function(fn, scope){
12685         var els = this.elements;
12686         var el = this.el;
12687         for(var i = 0, len = els.length; i < len; i++){
12688             el.dom = els[i];
12689                 if(fn.call(scope || el, el, this, i) === false){
12690                 break;
12691             }
12692         }
12693         return this;
12694     },
12695
12696     indexOf : function(el){
12697         return this.elements.indexOf(Roo.getDom(el));
12698     },
12699
12700     replaceElement : function(el, replacement, domReplace){
12701         var index = typeof el == 'number' ? el : this.indexOf(el);
12702         if(index !== -1){
12703             replacement = Roo.getDom(replacement);
12704             if(domReplace){
12705                 var d = this.elements[index];
12706                 d.parentNode.insertBefore(replacement, d);
12707                 d.parentNode.removeChild(d);
12708             }
12709             this.elements.splice(index, 1, replacement);
12710         }
12711         return this;
12712     }
12713 });
12714 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12715
12716 /*
12717  * Based on:
12718  * Ext JS Library 1.1.1
12719  * Copyright(c) 2006-2007, Ext JS, LLC.
12720  *
12721  * Originally Released Under LGPL - original licence link has changed is not relivant.
12722  *
12723  * Fork - LGPL
12724  * <script type="text/javascript">
12725  */
12726
12727  
12728
12729 /**
12730  * @class Roo.data.Connection
12731  * @extends Roo.util.Observable
12732  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12733  * either to a configured URL, or to a URL specified at request time. 
12734  * 
12735  * Requests made by this class are asynchronous, and will return immediately. No data from
12736  * the server will be available to the statement immediately following the {@link #request} call.
12737  * To process returned data, use a callback in the request options object, or an event listener.
12738  * 
12739  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12740  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12741  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12742  * property and, if present, the IFRAME's XML document as the responseXML property.
12743  * 
12744  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12745  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12746  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12747  * standard DOM methods.
12748  * @constructor
12749  * @param {Object} config a configuration object.
12750  */
12751 Roo.data.Connection = function(config){
12752     Roo.apply(this, config);
12753     this.addEvents({
12754         /**
12755          * @event beforerequest
12756          * Fires before a network request is made to retrieve a data object.
12757          * @param {Connection} conn This Connection object.
12758          * @param {Object} options The options config object passed to the {@link #request} method.
12759          */
12760         "beforerequest" : true,
12761         /**
12762          * @event requestcomplete
12763          * Fires if the request was successfully completed.
12764          * @param {Connection} conn This Connection object.
12765          * @param {Object} response The XHR object containing the response data.
12766          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12767          * @param {Object} options The options config object passed to the {@link #request} method.
12768          */
12769         "requestcomplete" : true,
12770         /**
12771          * @event requestexception
12772          * Fires if an error HTTP status was returned from the server.
12773          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12774          * @param {Connection} conn This Connection object.
12775          * @param {Object} response The XHR object containing the response data.
12776          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12777          * @param {Object} options The options config object passed to the {@link #request} method.
12778          */
12779         "requestexception" : true
12780     });
12781     Roo.data.Connection.superclass.constructor.call(this);
12782 };
12783
12784 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12785     /**
12786      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12787      */
12788     /**
12789      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12790      * extra parameters to each request made by this object. (defaults to undefined)
12791      */
12792     /**
12793      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12794      *  to each request made by this object. (defaults to undefined)
12795      */
12796     /**
12797      * @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)
12798      */
12799     /**
12800      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12801      */
12802     timeout : 30000,
12803     /**
12804      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12805      * @type Boolean
12806      */
12807     autoAbort:false,
12808
12809     /**
12810      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12811      * @type Boolean
12812      */
12813     disableCaching: true,
12814
12815     /**
12816      * Sends an HTTP request to a remote server.
12817      * @param {Object} options An object which may contain the following properties:<ul>
12818      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12819      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12820      * request, a url encoded string or a function to call to get either.</li>
12821      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12822      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12823      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12824      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12825      * <li>options {Object} The parameter to the request call.</li>
12826      * <li>success {Boolean} True if the request succeeded.</li>
12827      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12828      * </ul></li>
12829      * <li><b>success</b> {Function} (Optional) The function to be called upon success 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>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12835      * The callback is passed the following parameters:<ul>
12836      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12837      * <li>options {Object} The parameter to the request call.</li>
12838      * </ul></li>
12839      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12840      * for the callback function. Defaults to the browser window.</li>
12841      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12842      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12843      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12844      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12845      * params for the post data. Any params will be appended to the URL.</li>
12846      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12847      * </ul>
12848      * @return {Number} transactionId
12849      */
12850     request : function(o){
12851         if(this.fireEvent("beforerequest", this, o) !== false){
12852             var p = o.params;
12853
12854             if(typeof p == "function"){
12855                 p = p.call(o.scope||window, o);
12856             }
12857             if(typeof p == "object"){
12858                 p = Roo.urlEncode(o.params);
12859             }
12860             if(this.extraParams){
12861                 var extras = Roo.urlEncode(this.extraParams);
12862                 p = p ? (p + '&' + extras) : extras;
12863             }
12864
12865             var url = o.url || this.url;
12866             if(typeof url == 'function'){
12867                 url = url.call(o.scope||window, o);
12868             }
12869
12870             if(o.form){
12871                 var form = Roo.getDom(o.form);
12872                 url = url || form.action;
12873
12874                 var enctype = form.getAttribute("enctype");
12875                 
12876                 if (o.formData) {
12877                     return this.doFormDataUpload(o, url);
12878                 }
12879                 
12880                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12881                     return this.doFormUpload(o, p, url);
12882                 }
12883                 var f = Roo.lib.Ajax.serializeForm(form);
12884                 p = p ? (p + '&' + f) : f;
12885             }
12886             
12887             if (!o.form && o.formData) {
12888                 o.formData = o.formData === true ? new FormData() : o.formData;
12889                 for (var k in o.params) {
12890                     o.formData.append(k,o.params[k]);
12891                 }
12892                     
12893                 return this.doFormDataUpload(o, url);
12894             }
12895             
12896
12897             var hs = o.headers;
12898             if(this.defaultHeaders){
12899                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12900                 if(!o.headers){
12901                     o.headers = hs;
12902                 }
12903             }
12904
12905             var cb = {
12906                 success: this.handleResponse,
12907                 failure: this.handleFailure,
12908                 scope: this,
12909                 argument: {options: o},
12910                 timeout : o.timeout || this.timeout
12911             };
12912
12913             var method = o.method||this.method||(p ? "POST" : "GET");
12914
12915             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12916                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12917             }
12918
12919             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12920                 if(o.autoAbort){
12921                     this.abort();
12922                 }
12923             }else if(this.autoAbort !== false){
12924                 this.abort();
12925             }
12926
12927             if((method == 'GET' && p) || o.xmlData){
12928                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12929                 p = '';
12930             }
12931             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12932             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12933             Roo.lib.Ajax.useDefaultHeader == true;
12934             return this.transId;
12935         }else{
12936             Roo.callback(o.callback, o.scope, [o, null, null]);
12937             return null;
12938         }
12939     },
12940
12941     /**
12942      * Determine whether this object has a request outstanding.
12943      * @param {Number} transactionId (Optional) defaults to the last transaction
12944      * @return {Boolean} True if there is an outstanding request.
12945      */
12946     isLoading : function(transId){
12947         if(transId){
12948             return Roo.lib.Ajax.isCallInProgress(transId);
12949         }else{
12950             return this.transId ? true : false;
12951         }
12952     },
12953
12954     /**
12955      * Aborts any outstanding request.
12956      * @param {Number} transactionId (Optional) defaults to the last transaction
12957      */
12958     abort : function(transId){
12959         if(transId || this.isLoading()){
12960             Roo.lib.Ajax.abort(transId || this.transId);
12961         }
12962     },
12963
12964     // private
12965     handleResponse : function(response){
12966         this.transId = false;
12967         var options = response.argument.options;
12968         response.argument = options ? options.argument : null;
12969         this.fireEvent("requestcomplete", this, response, options);
12970         Roo.callback(options.success, options.scope, [response, options]);
12971         Roo.callback(options.callback, options.scope, [options, true, response]);
12972     },
12973
12974     // private
12975     handleFailure : function(response, e){
12976         this.transId = false;
12977         var options = response.argument.options;
12978         response.argument = options ? options.argument : null;
12979         this.fireEvent("requestexception", this, response, options, e);
12980         Roo.callback(options.failure, options.scope, [response, options]);
12981         Roo.callback(options.callback, options.scope, [options, false, response]);
12982     },
12983
12984     // private
12985     doFormUpload : function(o, ps, url){
12986         var id = Roo.id();
12987         var frame = document.createElement('iframe');
12988         frame.id = id;
12989         frame.name = id;
12990         frame.className = 'x-hidden';
12991         if(Roo.isIE){
12992             frame.src = Roo.SSL_SECURE_URL;
12993         }
12994         document.body.appendChild(frame);
12995
12996         if(Roo.isIE){
12997            document.frames[id].name = id;
12998         }
12999
13000         var form = Roo.getDom(o.form);
13001         form.target = id;
13002         form.method = 'POST';
13003         form.enctype = form.encoding = 'multipart/form-data';
13004         if(url){
13005             form.action = url;
13006         }
13007
13008         var hiddens, hd;
13009         if(ps){ // add dynamic params
13010             hiddens = [];
13011             ps = Roo.urlDecode(ps, false);
13012             for(var k in ps){
13013                 if(ps.hasOwnProperty(k)){
13014                     hd = document.createElement('input');
13015                     hd.type = 'hidden';
13016                     hd.name = k;
13017                     hd.value = ps[k];
13018                     form.appendChild(hd);
13019                     hiddens.push(hd);
13020                 }
13021             }
13022         }
13023
13024         function cb(){
13025             var r = {  // bogus response object
13026                 responseText : '',
13027                 responseXML : null
13028             };
13029
13030             r.argument = o ? o.argument : null;
13031
13032             try { //
13033                 var doc;
13034                 if(Roo.isIE){
13035                     doc = frame.contentWindow.document;
13036                 }else {
13037                     doc = (frame.contentDocument || window.frames[id].document);
13038                 }
13039                 if(doc && doc.body){
13040                     r.responseText = doc.body.innerHTML;
13041                 }
13042                 if(doc && doc.XMLDocument){
13043                     r.responseXML = doc.XMLDocument;
13044                 }else {
13045                     r.responseXML = doc;
13046                 }
13047             }
13048             catch(e) {
13049                 // ignore
13050             }
13051
13052             Roo.EventManager.removeListener(frame, 'load', cb, this);
13053
13054             this.fireEvent("requestcomplete", this, r, o);
13055             Roo.callback(o.success, o.scope, [r, o]);
13056             Roo.callback(o.callback, o.scope, [o, true, r]);
13057
13058             setTimeout(function(){document.body.removeChild(frame);}, 100);
13059         }
13060
13061         Roo.EventManager.on(frame, 'load', cb, this);
13062         form.submit();
13063
13064         if(hiddens){ // remove dynamic params
13065             for(var i = 0, len = hiddens.length; i < len; i++){
13066                 form.removeChild(hiddens[i]);
13067             }
13068         }
13069     },
13070     // this is a 'formdata version???'
13071     
13072     
13073     doFormDataUpload : function(o,  url)
13074     {
13075         var formData;
13076         if (o.form) {
13077             var form =  Roo.getDom(o.form);
13078             form.enctype = form.encoding = 'multipart/form-data';
13079             formData = o.formData === true ? new FormData(form) : o.formData;
13080         } else {
13081             formData = o.formData === true ? new FormData() : o.formData;
13082         }
13083         
13084       
13085         var cb = {
13086             success: this.handleResponse,
13087             failure: this.handleFailure,
13088             scope: this,
13089             argument: {options: o},
13090             timeout : o.timeout || this.timeout
13091         };
13092  
13093         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13094             if(o.autoAbort){
13095                 this.abort();
13096             }
13097         }else if(this.autoAbort !== false){
13098             this.abort();
13099         }
13100
13101         //Roo.lib.Ajax.defaultPostHeader = null;
13102         Roo.lib.Ajax.useDefaultHeader = false;
13103         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13104         Roo.lib.Ajax.useDefaultHeader = true;
13105  
13106          
13107     }
13108     
13109 });
13110 /*
13111  * Based on:
13112  * Ext JS Library 1.1.1
13113  * Copyright(c) 2006-2007, Ext JS, LLC.
13114  *
13115  * Originally Released Under LGPL - original licence link has changed is not relivant.
13116  *
13117  * Fork - LGPL
13118  * <script type="text/javascript">
13119  */
13120  
13121 /**
13122  * Global Ajax request class.
13123  * 
13124  * @class Roo.Ajax
13125  * @extends Roo.data.Connection
13126  * @static
13127  * 
13128  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13129  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13130  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13131  * @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)
13132  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13133  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13134  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13135  */
13136 Roo.Ajax = new Roo.data.Connection({
13137     // fix up the docs
13138     /**
13139      * @scope Roo.Ajax
13140      * @type {Boolear} 
13141      */
13142     autoAbort : false,
13143
13144     /**
13145      * Serialize the passed form into a url encoded string
13146      * @scope Roo.Ajax
13147      * @param {String/HTMLElement} form
13148      * @return {String}
13149      */
13150     serializeForm : function(form){
13151         return Roo.lib.Ajax.serializeForm(form);
13152     }
13153 });/*
13154  * Based on:
13155  * Ext JS Library 1.1.1
13156  * Copyright(c) 2006-2007, Ext JS, LLC.
13157  *
13158  * Originally Released Under LGPL - original licence link has changed is not relivant.
13159  *
13160  * Fork - LGPL
13161  * <script type="text/javascript">
13162  */
13163
13164  
13165 /**
13166  * @class Roo.UpdateManager
13167  * @extends Roo.util.Observable
13168  * Provides AJAX-style update for Element object.<br><br>
13169  * Usage:<br>
13170  * <pre><code>
13171  * // Get it from a Roo.Element object
13172  * var el = Roo.get("foo");
13173  * var mgr = el.getUpdateManager();
13174  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13175  * ...
13176  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13177  * <br>
13178  * // or directly (returns the same UpdateManager instance)
13179  * var mgr = new Roo.UpdateManager("myElementId");
13180  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13181  * mgr.on("update", myFcnNeedsToKnow);
13182  * <br>
13183    // short handed call directly from the element object
13184    Roo.get("foo").load({
13185         url: "bar.php",
13186         scripts:true,
13187         params: "for=bar",
13188         text: "Loading Foo..."
13189    });
13190  * </code></pre>
13191  * @constructor
13192  * Create new UpdateManager directly.
13193  * @param {String/HTMLElement/Roo.Element} el The element to update
13194  * @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).
13195  */
13196 Roo.UpdateManager = function(el, forceNew){
13197     el = Roo.get(el);
13198     if(!forceNew && el.updateManager){
13199         return el.updateManager;
13200     }
13201     /**
13202      * The Element object
13203      * @type Roo.Element
13204      */
13205     this.el = el;
13206     /**
13207      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13208      * @type String
13209      */
13210     this.defaultUrl = null;
13211
13212     this.addEvents({
13213         /**
13214          * @event beforeupdate
13215          * Fired before an update is made, return false from your handler and the update is cancelled.
13216          * @param {Roo.Element} el
13217          * @param {String/Object/Function} url
13218          * @param {String/Object} params
13219          */
13220         "beforeupdate": true,
13221         /**
13222          * @event update
13223          * Fired after successful update is made.
13224          * @param {Roo.Element} el
13225          * @param {Object} oResponseObject The response Object
13226          */
13227         "update": true,
13228         /**
13229          * @event failure
13230          * Fired on update failure.
13231          * @param {Roo.Element} el
13232          * @param {Object} oResponseObject The response Object
13233          */
13234         "failure": true
13235     });
13236     var d = Roo.UpdateManager.defaults;
13237     /**
13238      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13239      * @type String
13240      */
13241     this.sslBlankUrl = d.sslBlankUrl;
13242     /**
13243      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13244      * @type Boolean
13245      */
13246     this.disableCaching = d.disableCaching;
13247     /**
13248      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13249      * @type String
13250      */
13251     this.indicatorText = d.indicatorText;
13252     /**
13253      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13254      * @type String
13255      */
13256     this.showLoadIndicator = d.showLoadIndicator;
13257     /**
13258      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13259      * @type Number
13260      */
13261     this.timeout = d.timeout;
13262
13263     /**
13264      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13265      * @type Boolean
13266      */
13267     this.loadScripts = d.loadScripts;
13268
13269     /**
13270      * Transaction object of current executing transaction
13271      */
13272     this.transaction = null;
13273
13274     /**
13275      * @private
13276      */
13277     this.autoRefreshProcId = null;
13278     /**
13279      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13280      * @type Function
13281      */
13282     this.refreshDelegate = this.refresh.createDelegate(this);
13283     /**
13284      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13285      * @type Function
13286      */
13287     this.updateDelegate = this.update.createDelegate(this);
13288     /**
13289      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13290      * @type Function
13291      */
13292     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13293     /**
13294      * @private
13295      */
13296     this.successDelegate = this.processSuccess.createDelegate(this);
13297     /**
13298      * @private
13299      */
13300     this.failureDelegate = this.processFailure.createDelegate(this);
13301
13302     if(!this.renderer){
13303      /**
13304       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13305       */
13306     this.renderer = new Roo.UpdateManager.BasicRenderer();
13307     }
13308     
13309     Roo.UpdateManager.superclass.constructor.call(this);
13310 };
13311
13312 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13313     /**
13314      * Get the Element this UpdateManager is bound to
13315      * @return {Roo.Element} The element
13316      */
13317     getEl : function(){
13318         return this.el;
13319     },
13320     /**
13321      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13322      * @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:
13323 <pre><code>
13324 um.update({<br/>
13325     url: "your-url.php",<br/>
13326     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13327     callback: yourFunction,<br/>
13328     scope: yourObject, //(optional scope)  <br/>
13329     discardUrl: false, <br/>
13330     nocache: false,<br/>
13331     text: "Loading...",<br/>
13332     timeout: 30,<br/>
13333     scripts: false<br/>
13334 });
13335 </code></pre>
13336      * The only required property is url. The optional properties nocache, text and scripts
13337      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13338      * @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}
13339      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13340      * @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.
13341      */
13342     update : function(url, params, callback, discardUrl){
13343         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13344             var method = this.method,
13345                 cfg;
13346             if(typeof url == "object"){ // must be config object
13347                 cfg = url;
13348                 url = cfg.url;
13349                 params = params || cfg.params;
13350                 callback = callback || cfg.callback;
13351                 discardUrl = discardUrl || cfg.discardUrl;
13352                 if(callback && cfg.scope){
13353                     callback = callback.createDelegate(cfg.scope);
13354                 }
13355                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13356                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13357                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13358                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13359                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13360             }
13361             this.showLoading();
13362             if(!discardUrl){
13363                 this.defaultUrl = url;
13364             }
13365             if(typeof url == "function"){
13366                 url = url.call(this);
13367             }
13368
13369             method = method || (params ? "POST" : "GET");
13370             if(method == "GET"){
13371                 url = this.prepareUrl(url);
13372             }
13373
13374             var o = Roo.apply(cfg ||{}, {
13375                 url : url,
13376                 params: params,
13377                 success: this.successDelegate,
13378                 failure: this.failureDelegate,
13379                 callback: undefined,
13380                 timeout: (this.timeout*1000),
13381                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13382             });
13383             Roo.log("updated manager called with timeout of " + o.timeout);
13384             this.transaction = Roo.Ajax.request(o);
13385         }
13386     },
13387
13388     /**
13389      * 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.
13390      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13391      * @param {String/HTMLElement} form The form Id or form element
13392      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13393      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13394      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13395      */
13396     formUpdate : function(form, url, reset, callback){
13397         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13398             if(typeof url == "function"){
13399                 url = url.call(this);
13400             }
13401             form = Roo.getDom(form);
13402             this.transaction = Roo.Ajax.request({
13403                 form: form,
13404                 url:url,
13405                 success: this.successDelegate,
13406                 failure: this.failureDelegate,
13407                 timeout: (this.timeout*1000),
13408                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13409             });
13410             this.showLoading.defer(1, this);
13411         }
13412     },
13413
13414     /**
13415      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13416      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13417      */
13418     refresh : function(callback){
13419         if(this.defaultUrl == null){
13420             return;
13421         }
13422         this.update(this.defaultUrl, null, callback, true);
13423     },
13424
13425     /**
13426      * Set this element to auto refresh.
13427      * @param {Number} interval How often to update (in seconds).
13428      * @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)
13429      * @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}
13430      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13431      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13432      */
13433     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13434         if(refreshNow){
13435             this.update(url || this.defaultUrl, params, callback, true);
13436         }
13437         if(this.autoRefreshProcId){
13438             clearInterval(this.autoRefreshProcId);
13439         }
13440         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13441     },
13442
13443     /**
13444      * Stop auto refresh on this element.
13445      */
13446      stopAutoRefresh : function(){
13447         if(this.autoRefreshProcId){
13448             clearInterval(this.autoRefreshProcId);
13449             delete this.autoRefreshProcId;
13450         }
13451     },
13452
13453     isAutoRefreshing : function(){
13454        return this.autoRefreshProcId ? true : false;
13455     },
13456     /**
13457      * Called to update the element to "Loading" state. Override to perform custom action.
13458      */
13459     showLoading : function(){
13460         if(this.showLoadIndicator){
13461             this.el.update(this.indicatorText);
13462         }
13463     },
13464
13465     /**
13466      * Adds unique parameter to query string if disableCaching = true
13467      * @private
13468      */
13469     prepareUrl : function(url){
13470         if(this.disableCaching){
13471             var append = "_dc=" + (new Date().getTime());
13472             if(url.indexOf("?") !== -1){
13473                 url += "&" + append;
13474             }else{
13475                 url += "?" + append;
13476             }
13477         }
13478         return url;
13479     },
13480
13481     /**
13482      * @private
13483      */
13484     processSuccess : function(response){
13485         this.transaction = null;
13486         if(response.argument.form && response.argument.reset){
13487             try{ // put in try/catch since some older FF releases had problems with this
13488                 response.argument.form.reset();
13489             }catch(e){}
13490         }
13491         if(this.loadScripts){
13492             this.renderer.render(this.el, response, this,
13493                 this.updateComplete.createDelegate(this, [response]));
13494         }else{
13495             this.renderer.render(this.el, response, this);
13496             this.updateComplete(response);
13497         }
13498     },
13499
13500     updateComplete : function(response){
13501         this.fireEvent("update", this.el, response);
13502         if(typeof response.argument.callback == "function"){
13503             response.argument.callback(this.el, true, response);
13504         }
13505     },
13506
13507     /**
13508      * @private
13509      */
13510     processFailure : function(response){
13511         this.transaction = null;
13512         this.fireEvent("failure", this.el, response);
13513         if(typeof response.argument.callback == "function"){
13514             response.argument.callback(this.el, false, response);
13515         }
13516     },
13517
13518     /**
13519      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13520      * @param {Object} renderer The object implementing the render() method
13521      */
13522     setRenderer : function(renderer){
13523         this.renderer = renderer;
13524     },
13525
13526     getRenderer : function(){
13527        return this.renderer;
13528     },
13529
13530     /**
13531      * Set the defaultUrl used for updates
13532      * @param {String/Function} defaultUrl The url or a function to call to get the url
13533      */
13534     setDefaultUrl : function(defaultUrl){
13535         this.defaultUrl = defaultUrl;
13536     },
13537
13538     /**
13539      * Aborts the executing transaction
13540      */
13541     abort : function(){
13542         if(this.transaction){
13543             Roo.Ajax.abort(this.transaction);
13544         }
13545     },
13546
13547     /**
13548      * Returns true if an update is in progress
13549      * @return {Boolean}
13550      */
13551     isUpdating : function(){
13552         if(this.transaction){
13553             return Roo.Ajax.isLoading(this.transaction);
13554         }
13555         return false;
13556     }
13557 });
13558
13559 /**
13560  * @class Roo.UpdateManager.defaults
13561  * @static (not really - but it helps the doc tool)
13562  * The defaults collection enables customizing the default properties of UpdateManager
13563  */
13564    Roo.UpdateManager.defaults = {
13565        /**
13566          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13567          * @type Number
13568          */
13569          timeout : 30,
13570
13571          /**
13572          * True to process scripts by default (Defaults to false).
13573          * @type Boolean
13574          */
13575         loadScripts : false,
13576
13577         /**
13578         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13579         * @type String
13580         */
13581         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13582         /**
13583          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13584          * @type Boolean
13585          */
13586         disableCaching : false,
13587         /**
13588          * Whether to show indicatorText when loading (Defaults to true).
13589          * @type Boolean
13590          */
13591         showLoadIndicator : true,
13592         /**
13593          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13594          * @type String
13595          */
13596         indicatorText : '<div class="loading-indicator">Loading...</div>'
13597    };
13598
13599 /**
13600  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13601  *Usage:
13602  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13603  * @param {String/HTMLElement/Roo.Element} el The element to update
13604  * @param {String} url The url
13605  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13606  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13607  * @static
13608  * @deprecated
13609  * @member Roo.UpdateManager
13610  */
13611 Roo.UpdateManager.updateElement = function(el, url, params, options){
13612     var um = Roo.get(el, true).getUpdateManager();
13613     Roo.apply(um, options);
13614     um.update(url, params, options ? options.callback : null);
13615 };
13616 // alias for backwards compat
13617 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13618 /**
13619  * @class Roo.UpdateManager.BasicRenderer
13620  * Default Content renderer. Updates the elements innerHTML with the responseText.
13621  */
13622 Roo.UpdateManager.BasicRenderer = function(){};
13623
13624 Roo.UpdateManager.BasicRenderer.prototype = {
13625     /**
13626      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13627      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13628      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13629      * @param {Roo.Element} el The element being rendered
13630      * @param {Object} response The YUI Connect response object
13631      * @param {UpdateManager} updateManager The calling update manager
13632      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13633      */
13634      render : function(el, response, updateManager, callback){
13635         el.update(response.responseText, updateManager.loadScripts, callback);
13636     }
13637 };
13638 /*
13639  * Based on:
13640  * Roo JS
13641  * (c)) Alan Knowles
13642  * Licence : LGPL
13643  */
13644
13645
13646 /**
13647  * @class Roo.DomTemplate
13648  * @extends Roo.Template
13649  * An effort at a dom based template engine..
13650  *
13651  * Similar to XTemplate, except it uses dom parsing to create the template..
13652  *
13653  * Supported features:
13654  *
13655  *  Tags:
13656
13657 <pre><code>
13658       {a_variable} - output encoded.
13659       {a_variable.format:("Y-m-d")} - call a method on the variable
13660       {a_variable:raw} - unencoded output
13661       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13662       {a_variable:this.method_on_template(...)} - call a method on the template object.
13663  
13664 </code></pre>
13665  *  The tpl tag:
13666 <pre><code>
13667         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13668         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13669         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13670         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13671   
13672 </code></pre>
13673  *      
13674  */
13675 Roo.DomTemplate = function()
13676 {
13677      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13678      if (this.html) {
13679         this.compile();
13680      }
13681 };
13682
13683
13684 Roo.extend(Roo.DomTemplate, Roo.Template, {
13685     /**
13686      * id counter for sub templates.
13687      */
13688     id : 0,
13689     /**
13690      * flag to indicate if dom parser is inside a pre,
13691      * it will strip whitespace if not.
13692      */
13693     inPre : false,
13694     
13695     /**
13696      * The various sub templates
13697      */
13698     tpls : false,
13699     
13700     
13701     
13702     /**
13703      *
13704      * basic tag replacing syntax
13705      * WORD:WORD()
13706      *
13707      * // you can fake an object call by doing this
13708      *  x.t:(test,tesT) 
13709      * 
13710      */
13711     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13712     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13713     
13714     iterChild : function (node, method) {
13715         
13716         var oldPre = this.inPre;
13717         if (node.tagName == 'PRE') {
13718             this.inPre = true;
13719         }
13720         for( var i = 0; i < node.childNodes.length; i++) {
13721             method.call(this, node.childNodes[i]);
13722         }
13723         this.inPre = oldPre;
13724     },
13725     
13726     
13727     
13728     /**
13729      * compile the template
13730      *
13731      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13732      *
13733      */
13734     compile: function()
13735     {
13736         var s = this.html;
13737         
13738         // covert the html into DOM...
13739         var doc = false;
13740         var div =false;
13741         try {
13742             doc = document.implementation.createHTMLDocument("");
13743             doc.documentElement.innerHTML =   this.html  ;
13744             div = doc.documentElement;
13745         } catch (e) {
13746             // old IE... - nasty -- it causes all sorts of issues.. with
13747             // images getting pulled from server..
13748             div = document.createElement('div');
13749             div.innerHTML = this.html;
13750         }
13751         //doc.documentElement.innerHTML = htmlBody
13752          
13753         
13754         
13755         this.tpls = [];
13756         var _t = this;
13757         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13758         
13759         var tpls = this.tpls;
13760         
13761         // create a top level template from the snippet..
13762         
13763         //Roo.log(div.innerHTML);
13764         
13765         var tpl = {
13766             uid : 'master',
13767             id : this.id++,
13768             attr : false,
13769             value : false,
13770             body : div.innerHTML,
13771             
13772             forCall : false,
13773             execCall : false,
13774             dom : div,
13775             isTop : true
13776             
13777         };
13778         tpls.unshift(tpl);
13779         
13780         
13781         // compile them...
13782         this.tpls = [];
13783         Roo.each(tpls, function(tp){
13784             this.compileTpl(tp);
13785             this.tpls[tp.id] = tp;
13786         }, this);
13787         
13788         this.master = tpls[0];
13789         return this;
13790         
13791         
13792     },
13793     
13794     compileNode : function(node, istop) {
13795         // test for
13796         //Roo.log(node);
13797         
13798         
13799         // skip anything not a tag..
13800         if (node.nodeType != 1) {
13801             if (node.nodeType == 3 && !this.inPre) {
13802                 // reduce white space..
13803                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13804                 
13805             }
13806             return;
13807         }
13808         
13809         var tpl = {
13810             uid : false,
13811             id : false,
13812             attr : false,
13813             value : false,
13814             body : '',
13815             
13816             forCall : false,
13817             execCall : false,
13818             dom : false,
13819             isTop : istop
13820             
13821             
13822         };
13823         
13824         
13825         switch(true) {
13826             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13827             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13828             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13829             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13830             // no default..
13831         }
13832         
13833         
13834         if (!tpl.attr) {
13835             // just itterate children..
13836             this.iterChild(node,this.compileNode);
13837             return;
13838         }
13839         tpl.uid = this.id++;
13840         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13841         node.removeAttribute('roo-'+ tpl.attr);
13842         if (tpl.attr != 'name') {
13843             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13844             node.parentNode.replaceChild(placeholder,  node);
13845         } else {
13846             
13847             var placeholder =  document.createElement('span');
13848             placeholder.className = 'roo-tpl-' + tpl.value;
13849             node.parentNode.replaceChild(placeholder,  node);
13850         }
13851         
13852         // parent now sees '{domtplXXXX}
13853         this.iterChild(node,this.compileNode);
13854         
13855         // we should now have node body...
13856         var div = document.createElement('div');
13857         div.appendChild(node);
13858         tpl.dom = node;
13859         // this has the unfortunate side effect of converting tagged attributes
13860         // eg. href="{...}" into %7C...%7D
13861         // this has been fixed by searching for those combo's although it's a bit hacky..
13862         
13863         
13864         tpl.body = div.innerHTML;
13865         
13866         
13867          
13868         tpl.id = tpl.uid;
13869         switch(tpl.attr) {
13870             case 'for' :
13871                 switch (tpl.value) {
13872                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13873                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13874                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13875                 }
13876                 break;
13877             
13878             case 'exec':
13879                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13880                 break;
13881             
13882             case 'if':     
13883                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13884                 break;
13885             
13886             case 'name':
13887                 tpl.id  = tpl.value; // replace non characters???
13888                 break;
13889             
13890         }
13891         
13892         
13893         this.tpls.push(tpl);
13894         
13895         
13896         
13897     },
13898     
13899     
13900     
13901     
13902     /**
13903      * Compile a segment of the template into a 'sub-template'
13904      *
13905      * 
13906      * 
13907      *
13908      */
13909     compileTpl : function(tpl)
13910     {
13911         var fm = Roo.util.Format;
13912         var useF = this.disableFormats !== true;
13913         
13914         var sep = Roo.isGecko ? "+\n" : ",\n";
13915         
13916         var undef = function(str) {
13917             Roo.debug && Roo.log("Property not found :"  + str);
13918             return '';
13919         };
13920           
13921         //Roo.log(tpl.body);
13922         
13923         
13924         
13925         var fn = function(m, lbrace, name, format, args)
13926         {
13927             //Roo.log("ARGS");
13928             //Roo.log(arguments);
13929             args = args ? args.replace(/\\'/g,"'") : args;
13930             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13931             if (typeof(format) == 'undefined') {
13932                 format =  'htmlEncode'; 
13933             }
13934             if (format == 'raw' ) {
13935                 format = false;
13936             }
13937             
13938             if(name.substr(0, 6) == 'domtpl'){
13939                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13940             }
13941             
13942             // build an array of options to determine if value is undefined..
13943             
13944             // basically get 'xxxx.yyyy' then do
13945             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13946             //    (function () { Roo.log("Property not found"); return ''; })() :
13947             //    ......
13948             
13949             var udef_ar = [];
13950             var lookfor = '';
13951             Roo.each(name.split('.'), function(st) {
13952                 lookfor += (lookfor.length ? '.': '') + st;
13953                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13954             });
13955             
13956             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13957             
13958             
13959             if(format && useF){
13960                 
13961                 args = args ? ',' + args : "";
13962                  
13963                 if(format.substr(0, 5) != "this."){
13964                     format = "fm." + format + '(';
13965                 }else{
13966                     format = 'this.call("'+ format.substr(5) + '", ';
13967                     args = ", values";
13968                 }
13969                 
13970                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13971             }
13972              
13973             if (args && args.length) {
13974                 // called with xxyx.yuu:(test,test)
13975                 // change to ()
13976                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13977             }
13978             // raw.. - :raw modifier..
13979             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13980             
13981         };
13982         var body;
13983         // branched to use + in gecko and [].join() in others
13984         if(Roo.isGecko){
13985             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13986                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13987                     "';};};";
13988         }else{
13989             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13990             body.push(tpl.body.replace(/(\r\n|\n)/g,
13991                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13992             body.push("'].join('');};};");
13993             body = body.join('');
13994         }
13995         
13996         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13997        
13998         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13999         eval(body);
14000         
14001         return this;
14002     },
14003      
14004     /**
14005      * same as applyTemplate, except it's done to one of the subTemplates
14006      * when using named templates, you can do:
14007      *
14008      * var str = pl.applySubTemplate('your-name', values);
14009      *
14010      * 
14011      * @param {Number} id of the template
14012      * @param {Object} values to apply to template
14013      * @param {Object} parent (normaly the instance of this object)
14014      */
14015     applySubTemplate : function(id, values, parent)
14016     {
14017         
14018         
14019         var t = this.tpls[id];
14020         
14021         
14022         try { 
14023             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14024                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14025                 return '';
14026             }
14027         } catch(e) {
14028             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14029             Roo.log(values);
14030           
14031             return '';
14032         }
14033         try { 
14034             
14035             if(t.execCall && t.execCall.call(this, values, parent)){
14036                 return '';
14037             }
14038         } catch(e) {
14039             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14040             Roo.log(values);
14041             return '';
14042         }
14043         
14044         try {
14045             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14046             parent = t.target ? values : parent;
14047             if(t.forCall && vs instanceof Array){
14048                 var buf = [];
14049                 for(var i = 0, len = vs.length; i < len; i++){
14050                     try {
14051                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14052                     } catch (e) {
14053                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14054                         Roo.log(e.body);
14055                         //Roo.log(t.compiled);
14056                         Roo.log(vs[i]);
14057                     }   
14058                 }
14059                 return buf.join('');
14060             }
14061         } catch (e) {
14062             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14063             Roo.log(values);
14064             return '';
14065         }
14066         try {
14067             return t.compiled.call(this, vs, parent);
14068         } catch (e) {
14069             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14070             Roo.log(e.body);
14071             //Roo.log(t.compiled);
14072             Roo.log(values);
14073             return '';
14074         }
14075     },
14076
14077    
14078
14079     applyTemplate : function(values){
14080         return this.master.compiled.call(this, values, {});
14081         //var s = this.subs;
14082     },
14083
14084     apply : function(){
14085         return this.applyTemplate.apply(this, arguments);
14086     }
14087
14088  });
14089
14090 Roo.DomTemplate.from = function(el){
14091     el = Roo.getDom(el);
14092     return new Roo.Domtemplate(el.value || el.innerHTML);
14093 };/*
14094  * Based on:
14095  * Ext JS Library 1.1.1
14096  * Copyright(c) 2006-2007, Ext JS, LLC.
14097  *
14098  * Originally Released Under LGPL - original licence link has changed is not relivant.
14099  *
14100  * Fork - LGPL
14101  * <script type="text/javascript">
14102  */
14103
14104 /**
14105  * @class Roo.util.DelayedTask
14106  * Provides a convenient method of performing setTimeout where a new
14107  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14108  * You can use this class to buffer
14109  * the keypress events for a certain number of milliseconds, and perform only if they stop
14110  * for that amount of time.
14111  * @constructor The parameters to this constructor serve as defaults and are not required.
14112  * @param {Function} fn (optional) The default function to timeout
14113  * @param {Object} scope (optional) The default scope of that timeout
14114  * @param {Array} args (optional) The default Array of arguments
14115  */
14116 Roo.util.DelayedTask = function(fn, scope, args){
14117     var id = null, d, t;
14118
14119     var call = function(){
14120         var now = new Date().getTime();
14121         if(now - t >= d){
14122             clearInterval(id);
14123             id = null;
14124             fn.apply(scope, args || []);
14125         }
14126     };
14127     /**
14128      * Cancels any pending timeout and queues a new one
14129      * @param {Number} delay The milliseconds to delay
14130      * @param {Function} newFn (optional) Overrides function passed to constructor
14131      * @param {Object} newScope (optional) Overrides scope passed to constructor
14132      * @param {Array} newArgs (optional) Overrides args passed to constructor
14133      */
14134     this.delay = function(delay, newFn, newScope, newArgs){
14135         if(id && delay != d){
14136             this.cancel();
14137         }
14138         d = delay;
14139         t = new Date().getTime();
14140         fn = newFn || fn;
14141         scope = newScope || scope;
14142         args = newArgs || args;
14143         if(!id){
14144             id = setInterval(call, d);
14145         }
14146     };
14147
14148     /**
14149      * Cancel the last queued timeout
14150      */
14151     this.cancel = function(){
14152         if(id){
14153             clearInterval(id);
14154             id = null;
14155         }
14156     };
14157 };/*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.util.TaskRunner
14169  * Manage background tasks - not sure why this is better that setInterval?
14170  * @static
14171  *
14172  */
14173  
14174 Roo.util.TaskRunner = function(interval){
14175     interval = interval || 10;
14176     var tasks = [], removeQueue = [];
14177     var id = 0;
14178     var running = false;
14179
14180     var stopThread = function(){
14181         running = false;
14182         clearInterval(id);
14183         id = 0;
14184     };
14185
14186     var startThread = function(){
14187         if(!running){
14188             running = true;
14189             id = setInterval(runTasks, interval);
14190         }
14191     };
14192
14193     var removeTask = function(task){
14194         removeQueue.push(task);
14195         if(task.onStop){
14196             task.onStop();
14197         }
14198     };
14199
14200     var runTasks = function(){
14201         if(removeQueue.length > 0){
14202             for(var i = 0, len = removeQueue.length; i < len; i++){
14203                 tasks.remove(removeQueue[i]);
14204             }
14205             removeQueue = [];
14206             if(tasks.length < 1){
14207                 stopThread();
14208                 return;
14209             }
14210         }
14211         var now = new Date().getTime();
14212         for(var i = 0, len = tasks.length; i < len; ++i){
14213             var t = tasks[i];
14214             var itime = now - t.taskRunTime;
14215             if(t.interval <= itime){
14216                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14217                 t.taskRunTime = now;
14218                 if(rt === false || t.taskRunCount === t.repeat){
14219                     removeTask(t);
14220                     return;
14221                 }
14222             }
14223             if(t.duration && t.duration <= (now - t.taskStartTime)){
14224                 removeTask(t);
14225             }
14226         }
14227     };
14228
14229     /**
14230      * Queues a new task.
14231      * @param {Object} task
14232      *
14233      * Task property : interval = how frequent to run.
14234      * Task object should implement
14235      * function run()
14236      * Task object may implement
14237      * function onStop()
14238      */
14239     this.start = function(task){
14240         tasks.push(task);
14241         task.taskStartTime = new Date().getTime();
14242         task.taskRunTime = 0;
14243         task.taskRunCount = 0;
14244         startThread();
14245         return task;
14246     };
14247     /**
14248      * Stop  new task.
14249      * @param {Object} task
14250      */
14251     this.stop = function(task){
14252         removeTask(task);
14253         return task;
14254     };
14255     /**
14256      * Stop all Tasks
14257      */
14258     this.stopAll = function(){
14259         stopThread();
14260         for(var i = 0, len = tasks.length; i < len; i++){
14261             if(tasks[i].onStop){
14262                 tasks[i].onStop();
14263             }
14264         }
14265         tasks = [];
14266         removeQueue = [];
14267     };
14268 };
14269
14270 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14271  * Based on:
14272  * Ext JS Library 1.1.1
14273  * Copyright(c) 2006-2007, Ext JS, LLC.
14274  *
14275  * Originally Released Under LGPL - original licence link has changed is not relivant.
14276  *
14277  * Fork - LGPL
14278  * <script type="text/javascript">
14279  */
14280
14281  
14282 /**
14283  * @class Roo.util.MixedCollection
14284  * @extends Roo.util.Observable
14285  * A Collection class that maintains both numeric indexes and keys and exposes events.
14286  * @constructor
14287  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14288  * collection (defaults to false)
14289  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14290  * and return the key value for that item.  This is used when available to look up the key on items that
14291  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14292  * equivalent to providing an implementation for the {@link #getKey} method.
14293  */
14294 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14295     this.items = [];
14296     this.map = {};
14297     this.keys = [];
14298     this.length = 0;
14299     this.addEvents({
14300         /**
14301          * @event clear
14302          * Fires when the collection is cleared.
14303          */
14304         "clear" : true,
14305         /**
14306          * @event add
14307          * Fires when an item is added to the collection.
14308          * @param {Number} index The index at which the item was added.
14309          * @param {Object} o The item added.
14310          * @param {String} key The key associated with the added item.
14311          */
14312         "add" : true,
14313         /**
14314          * @event replace
14315          * Fires when an item is replaced in the collection.
14316          * @param {String} key he key associated with the new added.
14317          * @param {Object} old The item being replaced.
14318          * @param {Object} new The new item.
14319          */
14320         "replace" : true,
14321         /**
14322          * @event remove
14323          * Fires when an item is removed from the collection.
14324          * @param {Object} o The item being removed.
14325          * @param {String} key (optional) The key associated with the removed item.
14326          */
14327         "remove" : true,
14328         "sort" : true
14329     });
14330     this.allowFunctions = allowFunctions === true;
14331     if(keyFn){
14332         this.getKey = keyFn;
14333     }
14334     Roo.util.MixedCollection.superclass.constructor.call(this);
14335 };
14336
14337 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14338     allowFunctions : false,
14339     
14340 /**
14341  * Adds an item to the collection.
14342  * @param {String} key The key to associate with the item
14343  * @param {Object} o The item to add.
14344  * @return {Object} The item added.
14345  */
14346     add : function(key, o){
14347         if(arguments.length == 1){
14348             o = arguments[0];
14349             key = this.getKey(o);
14350         }
14351         if(typeof key == "undefined" || key === null){
14352             this.length++;
14353             this.items.push(o);
14354             this.keys.push(null);
14355         }else{
14356             var old = this.map[key];
14357             if(old){
14358                 return this.replace(key, o);
14359             }
14360             this.length++;
14361             this.items.push(o);
14362             this.map[key] = o;
14363             this.keys.push(key);
14364         }
14365         this.fireEvent("add", this.length-1, o, key);
14366         return o;
14367     },
14368        
14369 /**
14370   * MixedCollection has a generic way to fetch keys if you implement getKey.
14371 <pre><code>
14372 // normal way
14373 var mc = new Roo.util.MixedCollection();
14374 mc.add(someEl.dom.id, someEl);
14375 mc.add(otherEl.dom.id, otherEl);
14376 //and so on
14377
14378 // using getKey
14379 var mc = new Roo.util.MixedCollection();
14380 mc.getKey = function(el){
14381    return el.dom.id;
14382 };
14383 mc.add(someEl);
14384 mc.add(otherEl);
14385
14386 // or via the constructor
14387 var mc = new Roo.util.MixedCollection(false, function(el){
14388    return el.dom.id;
14389 });
14390 mc.add(someEl);
14391 mc.add(otherEl);
14392 </code></pre>
14393  * @param o {Object} The item for which to find the key.
14394  * @return {Object} The key for the passed item.
14395  */
14396     getKey : function(o){
14397          return o.id; 
14398     },
14399    
14400 /**
14401  * Replaces an item in the collection.
14402  * @param {String} key The key associated with the item to replace, or the item to replace.
14403  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14404  * @return {Object}  The new item.
14405  */
14406     replace : function(key, o){
14407         if(arguments.length == 1){
14408             o = arguments[0];
14409             key = this.getKey(o);
14410         }
14411         var old = this.item(key);
14412         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14413              return this.add(key, o);
14414         }
14415         var index = this.indexOfKey(key);
14416         this.items[index] = o;
14417         this.map[key] = o;
14418         this.fireEvent("replace", key, old, o);
14419         return o;
14420     },
14421    
14422 /**
14423  * Adds all elements of an Array or an Object to the collection.
14424  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14425  * an Array of values, each of which are added to the collection.
14426  */
14427     addAll : function(objs){
14428         if(arguments.length > 1 || objs instanceof Array){
14429             var args = arguments.length > 1 ? arguments : objs;
14430             for(var i = 0, len = args.length; i < len; i++){
14431                 this.add(args[i]);
14432             }
14433         }else{
14434             for(var key in objs){
14435                 if(this.allowFunctions || typeof objs[key] != "function"){
14436                     this.add(key, objs[key]);
14437                 }
14438             }
14439         }
14440     },
14441    
14442 /**
14443  * Executes the specified function once for every item in the collection, passing each
14444  * item as the first and only parameter. returning false from the function will stop the iteration.
14445  * @param {Function} fn The function to execute for each item.
14446  * @param {Object} scope (optional) The scope in which to execute the function.
14447  */
14448     each : function(fn, scope){
14449         var items = [].concat(this.items); // each safe for removal
14450         for(var i = 0, len = items.length; i < len; i++){
14451             if(fn.call(scope || items[i], items[i], i, len) === false){
14452                 break;
14453             }
14454         }
14455     },
14456    
14457 /**
14458  * Executes the specified function once for every key in the collection, passing each
14459  * key, and its associated item as the first two parameters.
14460  * @param {Function} fn The function to execute for each item.
14461  * @param {Object} scope (optional) The scope in which to execute the function.
14462  */
14463     eachKey : function(fn, scope){
14464         for(var i = 0, len = this.keys.length; i < len; i++){
14465             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14466         }
14467     },
14468    
14469 /**
14470  * Returns the first item in the collection which elicits a true return value from the
14471  * passed selection function.
14472  * @param {Function} fn The selection function to execute for each item.
14473  * @param {Object} scope (optional) The scope in which to execute the function.
14474  * @return {Object} The first item in the collection which returned true from the selection function.
14475  */
14476     find : function(fn, scope){
14477         for(var i = 0, len = this.items.length; i < len; i++){
14478             if(fn.call(scope || window, this.items[i], this.keys[i])){
14479                 return this.items[i];
14480             }
14481         }
14482         return null;
14483     },
14484    
14485 /**
14486  * Inserts an item at the specified index in the collection.
14487  * @param {Number} index The index to insert the item at.
14488  * @param {String} key The key to associate with the new item, or the item itself.
14489  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14490  * @return {Object} The item inserted.
14491  */
14492     insert : function(index, key, o){
14493         if(arguments.length == 2){
14494             o = arguments[1];
14495             key = this.getKey(o);
14496         }
14497         if(index >= this.length){
14498             return this.add(key, o);
14499         }
14500         this.length++;
14501         this.items.splice(index, 0, o);
14502         if(typeof key != "undefined" && key != null){
14503             this.map[key] = o;
14504         }
14505         this.keys.splice(index, 0, key);
14506         this.fireEvent("add", index, o, key);
14507         return o;
14508     },
14509    
14510 /**
14511  * Removed an item from the collection.
14512  * @param {Object} o The item to remove.
14513  * @return {Object} The item removed.
14514  */
14515     remove : function(o){
14516         return this.removeAt(this.indexOf(o));
14517     },
14518    
14519 /**
14520  * Remove an item from a specified index in the collection.
14521  * @param {Number} index The index within the collection of the item to remove.
14522  */
14523     removeAt : function(index){
14524         if(index < this.length && index >= 0){
14525             this.length--;
14526             var o = this.items[index];
14527             this.items.splice(index, 1);
14528             var key = this.keys[index];
14529             if(typeof key != "undefined"){
14530                 delete this.map[key];
14531             }
14532             this.keys.splice(index, 1);
14533             this.fireEvent("remove", o, key);
14534         }
14535     },
14536    
14537 /**
14538  * Removed an item associated with the passed key fom the collection.
14539  * @param {String} key The key of the item to remove.
14540  */
14541     removeKey : function(key){
14542         return this.removeAt(this.indexOfKey(key));
14543     },
14544    
14545 /**
14546  * Returns the number of items in the collection.
14547  * @return {Number} the number of items in the collection.
14548  */
14549     getCount : function(){
14550         return this.length; 
14551     },
14552    
14553 /**
14554  * Returns index within the collection of the passed Object.
14555  * @param {Object} o The item to find the index of.
14556  * @return {Number} index of the item.
14557  */
14558     indexOf : function(o){
14559         if(!this.items.indexOf){
14560             for(var i = 0, len = this.items.length; i < len; i++){
14561                 if(this.items[i] == o) {
14562                     return i;
14563                 }
14564             }
14565             return -1;
14566         }else{
14567             return this.items.indexOf(o);
14568         }
14569     },
14570    
14571 /**
14572  * Returns index within the collection of the passed key.
14573  * @param {String} key The key to find the index of.
14574  * @return {Number} index of the key.
14575  */
14576     indexOfKey : function(key){
14577         if(!this.keys.indexOf){
14578             for(var i = 0, len = this.keys.length; i < len; i++){
14579                 if(this.keys[i] == key) {
14580                     return i;
14581                 }
14582             }
14583             return -1;
14584         }else{
14585             return this.keys.indexOf(key);
14586         }
14587     },
14588    
14589 /**
14590  * Returns the item associated with the passed key OR index. Key has priority over index.
14591  * @param {String/Number} key The key or index of the item.
14592  * @return {Object} The item associated with the passed key.
14593  */
14594     item : function(key){
14595         if (key === 'length') {
14596             return null;
14597         }
14598         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14599         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14600     },
14601     
14602 /**
14603  * Returns the item at the specified index.
14604  * @param {Number} index The index of the item.
14605  * @return {Object}
14606  */
14607     itemAt : function(index){
14608         return this.items[index];
14609     },
14610     
14611 /**
14612  * Returns the item associated with the passed key.
14613  * @param {String/Number} key The key of the item.
14614  * @return {Object} The item associated with the passed key.
14615  */
14616     key : function(key){
14617         return this.map[key];
14618     },
14619    
14620 /**
14621  * Returns true if the collection contains the passed Object as an item.
14622  * @param {Object} o  The Object to look for in the collection.
14623  * @return {Boolean} True if the collection contains the Object as an item.
14624  */
14625     contains : function(o){
14626         return this.indexOf(o) != -1;
14627     },
14628    
14629 /**
14630  * Returns true if the collection contains the passed Object as a key.
14631  * @param {String} key The key to look for in the collection.
14632  * @return {Boolean} True if the collection contains the Object as a key.
14633  */
14634     containsKey : function(key){
14635         return typeof this.map[key] != "undefined";
14636     },
14637    
14638 /**
14639  * Removes all items from the collection.
14640  */
14641     clear : function(){
14642         this.length = 0;
14643         this.items = [];
14644         this.keys = [];
14645         this.map = {};
14646         this.fireEvent("clear");
14647     },
14648    
14649 /**
14650  * Returns the first item in the collection.
14651  * @return {Object} the first item in the collection..
14652  */
14653     first : function(){
14654         return this.items[0]; 
14655     },
14656    
14657 /**
14658  * Returns the last item in the collection.
14659  * @return {Object} the last item in the collection..
14660  */
14661     last : function(){
14662         return this.items[this.length-1];   
14663     },
14664     
14665     _sort : function(property, dir, fn){
14666         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14667         fn = fn || function(a, b){
14668             return a-b;
14669         };
14670         var c = [], k = this.keys, items = this.items;
14671         for(var i = 0, len = items.length; i < len; i++){
14672             c[c.length] = {key: k[i], value: items[i], index: i};
14673         }
14674         c.sort(function(a, b){
14675             var v = fn(a[property], b[property]) * dsc;
14676             if(v == 0){
14677                 v = (a.index < b.index ? -1 : 1);
14678             }
14679             return v;
14680         });
14681         for(var i = 0, len = c.length; i < len; i++){
14682             items[i] = c[i].value;
14683             k[i] = c[i].key;
14684         }
14685         this.fireEvent("sort", this);
14686     },
14687     
14688     /**
14689      * Sorts this collection with the passed comparison function
14690      * @param {String} direction (optional) "ASC" or "DESC"
14691      * @param {Function} fn (optional) comparison function
14692      */
14693     sort : function(dir, fn){
14694         this._sort("value", dir, fn);
14695     },
14696     
14697     /**
14698      * Sorts this collection by keys
14699      * @param {String} direction (optional) "ASC" or "DESC"
14700      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14701      */
14702     keySort : function(dir, fn){
14703         this._sort("key", dir, fn || function(a, b){
14704             return String(a).toUpperCase()-String(b).toUpperCase();
14705         });
14706     },
14707     
14708     /**
14709      * Returns a range of items in this collection
14710      * @param {Number} startIndex (optional) defaults to 0
14711      * @param {Number} endIndex (optional) default to the last item
14712      * @return {Array} An array of items
14713      */
14714     getRange : function(start, end){
14715         var items = this.items;
14716         if(items.length < 1){
14717             return [];
14718         }
14719         start = start || 0;
14720         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14721         var r = [];
14722         if(start <= end){
14723             for(var i = start; i <= end; i++) {
14724                     r[r.length] = items[i];
14725             }
14726         }else{
14727             for(var i = start; i >= end; i--) {
14728                     r[r.length] = items[i];
14729             }
14730         }
14731         return r;
14732     },
14733         
14734     /**
14735      * Filter the <i>objects</i> in this collection by a specific property. 
14736      * Returns a new collection that has been filtered.
14737      * @param {String} property A property on your objects
14738      * @param {String/RegExp} value Either string that the property values 
14739      * should start with or a RegExp to test against the property
14740      * @return {MixedCollection} The new filtered collection
14741      */
14742     filter : function(property, value){
14743         if(!value.exec){ // not a regex
14744             value = String(value);
14745             if(value.length == 0){
14746                 return this.clone();
14747             }
14748             value = new RegExp("^" + Roo.escapeRe(value), "i");
14749         }
14750         return this.filterBy(function(o){
14751             return o && value.test(o[property]);
14752         });
14753         },
14754     
14755     /**
14756      * Filter by a function. * Returns a new collection that has been filtered.
14757      * The passed function will be called with each 
14758      * object in the collection. If the function returns true, the value is included 
14759      * otherwise it is filtered.
14760      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14761      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14762      * @return {MixedCollection} The new filtered collection
14763      */
14764     filterBy : function(fn, scope){
14765         var r = new Roo.util.MixedCollection();
14766         r.getKey = this.getKey;
14767         var k = this.keys, it = this.items;
14768         for(var i = 0, len = it.length; i < len; i++){
14769             if(fn.call(scope||this, it[i], k[i])){
14770                                 r.add(k[i], it[i]);
14771                         }
14772         }
14773         return r;
14774     },
14775     
14776     /**
14777      * Creates a duplicate of this collection
14778      * @return {MixedCollection}
14779      */
14780     clone : function(){
14781         var r = new Roo.util.MixedCollection();
14782         var k = this.keys, it = this.items;
14783         for(var i = 0, len = it.length; i < len; i++){
14784             r.add(k[i], it[i]);
14785         }
14786         r.getKey = this.getKey;
14787         return r;
14788     }
14789 });
14790 /**
14791  * Returns the item associated with the passed key or index.
14792  * @method
14793  * @param {String/Number} key The key or index of the item.
14794  * @return {Object} The item associated with the passed key.
14795  */
14796 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14797  * Based on:
14798  * Ext JS Library 1.1.1
14799  * Copyright(c) 2006-2007, Ext JS, LLC.
14800  *
14801  * Originally Released Under LGPL - original licence link has changed is not relivant.
14802  *
14803  * Fork - LGPL
14804  * <script type="text/javascript">
14805  */
14806 /**
14807  * @class Roo.util.JSON
14808  * Modified version of Douglas Crockford"s json.js that doesn"t
14809  * mess with the Object prototype 
14810  * http://www.json.org/js.html
14811  * @static
14812  */
14813 Roo.util.JSON = new (function(){
14814     var useHasOwn = {}.hasOwnProperty ? true : false;
14815     
14816     // crashes Safari in some instances
14817     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14818     
14819     var pad = function(n) {
14820         return n < 10 ? "0" + n : n;
14821     };
14822     
14823     var m = {
14824         "\b": '\\b',
14825         "\t": '\\t',
14826         "\n": '\\n',
14827         "\f": '\\f',
14828         "\r": '\\r',
14829         '"' : '\\"',
14830         "\\": '\\\\'
14831     };
14832
14833     var encodeString = function(s){
14834         if (/["\\\x00-\x1f]/.test(s)) {
14835             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14836                 var c = m[b];
14837                 if(c){
14838                     return c;
14839                 }
14840                 c = b.charCodeAt();
14841                 return "\\u00" +
14842                     Math.floor(c / 16).toString(16) +
14843                     (c % 16).toString(16);
14844             }) + '"';
14845         }
14846         return '"' + s + '"';
14847     };
14848     
14849     var encodeArray = function(o){
14850         var a = ["["], b, i, l = o.length, v;
14851             for (i = 0; i < l; i += 1) {
14852                 v = o[i];
14853                 switch (typeof v) {
14854                     case "undefined":
14855                     case "function":
14856                     case "unknown":
14857                         break;
14858                     default:
14859                         if (b) {
14860                             a.push(',');
14861                         }
14862                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14863                         b = true;
14864                 }
14865             }
14866             a.push("]");
14867             return a.join("");
14868     };
14869     
14870     var encodeDate = function(o){
14871         return '"' + o.getFullYear() + "-" +
14872                 pad(o.getMonth() + 1) + "-" +
14873                 pad(o.getDate()) + "T" +
14874                 pad(o.getHours()) + ":" +
14875                 pad(o.getMinutes()) + ":" +
14876                 pad(o.getSeconds()) + '"';
14877     };
14878     
14879     /**
14880      * Encodes an Object, Array or other value
14881      * @param {Mixed} o The variable to encode
14882      * @return {String} The JSON string
14883      */
14884     this.encode = function(o)
14885     {
14886         // should this be extended to fully wrap stringify..
14887         
14888         if(typeof o == "undefined" || o === null){
14889             return "null";
14890         }else if(o instanceof Array){
14891             return encodeArray(o);
14892         }else if(o instanceof Date){
14893             return encodeDate(o);
14894         }else if(typeof o == "string"){
14895             return encodeString(o);
14896         }else if(typeof o == "number"){
14897             return isFinite(o) ? String(o) : "null";
14898         }else if(typeof o == "boolean"){
14899             return String(o);
14900         }else {
14901             var a = ["{"], b, i, v;
14902             for (i in o) {
14903                 if(!useHasOwn || o.hasOwnProperty(i)) {
14904                     v = o[i];
14905                     switch (typeof v) {
14906                     case "undefined":
14907                     case "function":
14908                     case "unknown":
14909                         break;
14910                     default:
14911                         if(b){
14912                             a.push(',');
14913                         }
14914                         a.push(this.encode(i), ":",
14915                                 v === null ? "null" : this.encode(v));
14916                         b = true;
14917                     }
14918                 }
14919             }
14920             a.push("}");
14921             return a.join("");
14922         }
14923     };
14924     
14925     /**
14926      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14927      * @param {String} json The JSON string
14928      * @return {Object} The resulting object
14929      */
14930     this.decode = function(json){
14931         
14932         return  /** eval:var:json */ eval("(" + json + ')');
14933     };
14934 })();
14935 /** 
14936  * Shorthand for {@link Roo.util.JSON#encode}
14937  * @member Roo encode 
14938  * @method */
14939 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14940 /** 
14941  * Shorthand for {@link Roo.util.JSON#decode}
14942  * @member Roo decode 
14943  * @method */
14944 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14945 /*
14946  * Based on:
14947  * Ext JS Library 1.1.1
14948  * Copyright(c) 2006-2007, Ext JS, LLC.
14949  *
14950  * Originally Released Under LGPL - original licence link has changed is not relivant.
14951  *
14952  * Fork - LGPL
14953  * <script type="text/javascript">
14954  */
14955  
14956 /**
14957  * @class Roo.util.Format
14958  * Reusable data formatting functions
14959  * @static
14960  */
14961 Roo.util.Format = function(){
14962     var trimRe = /^\s+|\s+$/g;
14963     return {
14964         /**
14965          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14966          * @param {String} value The string to truncate
14967          * @param {Number} length The maximum length to allow before truncating
14968          * @return {String} The converted text
14969          */
14970         ellipsis : function(value, len){
14971             if(value && value.length > len){
14972                 return value.substr(0, len-3)+"...";
14973             }
14974             return value;
14975         },
14976
14977         /**
14978          * Checks a reference and converts it to empty string if it is undefined
14979          * @param {Mixed} value Reference to check
14980          * @return {Mixed} Empty string if converted, otherwise the original value
14981          */
14982         undef : function(value){
14983             return typeof value != "undefined" ? value : "";
14984         },
14985
14986         /**
14987          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14988          * @param {String} value The string to encode
14989          * @return {String} The encoded text
14990          */
14991         htmlEncode : function(value){
14992             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14993         },
14994
14995         /**
14996          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14997          * @param {String} value The string to decode
14998          * @return {String} The decoded text
14999          */
15000         htmlDecode : function(value){
15001             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15002         },
15003
15004         /**
15005          * Trims any whitespace from either side of a string
15006          * @param {String} value The text to trim
15007          * @return {String} The trimmed text
15008          */
15009         trim : function(value){
15010             return String(value).replace(trimRe, "");
15011         },
15012
15013         /**
15014          * Returns a substring from within an original string
15015          * @param {String} value The original text
15016          * @param {Number} start The start index of the substring
15017          * @param {Number} length The length of the substring
15018          * @return {String} The substring
15019          */
15020         substr : function(value, start, length){
15021             return String(value).substr(start, length);
15022         },
15023
15024         /**
15025          * Converts a string to all lower case letters
15026          * @param {String} value The text to convert
15027          * @return {String} The converted text
15028          */
15029         lowercase : function(value){
15030             return String(value).toLowerCase();
15031         },
15032
15033         /**
15034          * Converts a string to all upper case letters
15035          * @param {String} value The text to convert
15036          * @return {String} The converted text
15037          */
15038         uppercase : function(value){
15039             return String(value).toUpperCase();
15040         },
15041
15042         /**
15043          * Converts the first character only of a string to upper case
15044          * @param {String} value The text to convert
15045          * @return {String} The converted text
15046          */
15047         capitalize : function(value){
15048             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15049         },
15050
15051         // private
15052         call : function(value, fn){
15053             if(arguments.length > 2){
15054                 var args = Array.prototype.slice.call(arguments, 2);
15055                 args.unshift(value);
15056                  
15057                 return /** eval:var:value */  eval(fn).apply(window, args);
15058             }else{
15059                 /** eval:var:value */
15060                 return /** eval:var:value */ eval(fn).call(window, value);
15061             }
15062         },
15063
15064        
15065         /**
15066          * safer version of Math.toFixed..??/
15067          * @param {Number/String} value The numeric value to format
15068          * @param {Number/String} value Decimal places 
15069          * @return {String} The formatted currency string
15070          */
15071         toFixed : function(v, n)
15072         {
15073             // why not use to fixed - precision is buggered???
15074             if (!n) {
15075                 return Math.round(v-0);
15076             }
15077             var fact = Math.pow(10,n+1);
15078             v = (Math.round((v-0)*fact))/fact;
15079             var z = (''+fact).substring(2);
15080             if (v == Math.floor(v)) {
15081                 return Math.floor(v) + '.' + z;
15082             }
15083             
15084             // now just padd decimals..
15085             var ps = String(v).split('.');
15086             var fd = (ps[1] + z);
15087             var r = fd.substring(0,n); 
15088             var rm = fd.substring(n); 
15089             if (rm < 5) {
15090                 return ps[0] + '.' + r;
15091             }
15092             r*=1; // turn it into a number;
15093             r++;
15094             if (String(r).length != n) {
15095                 ps[0]*=1;
15096                 ps[0]++;
15097                 r = String(r).substring(1); // chop the end off.
15098             }
15099             
15100             return ps[0] + '.' + r;
15101              
15102         },
15103         
15104         /**
15105          * Format a number as US currency
15106          * @param {Number/String} value The numeric value to format
15107          * @return {String} The formatted currency string
15108          */
15109         usMoney : function(v){
15110             return '$' + Roo.util.Format.number(v);
15111         },
15112         
15113         /**
15114          * Format a number
15115          * eventually this should probably emulate php's number_format
15116          * @param {Number/String} value The numeric value to format
15117          * @param {Number} decimals number of decimal places
15118          * @param {String} delimiter for thousands (default comma)
15119          * @return {String} The formatted currency string
15120          */
15121         number : function(v, decimals, thousandsDelimiter)
15122         {
15123             // multiply and round.
15124             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15125             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15126             
15127             var mul = Math.pow(10, decimals);
15128             var zero = String(mul).substring(1);
15129             v = (Math.round((v-0)*mul))/mul;
15130             
15131             // if it's '0' number.. then
15132             
15133             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15134             v = String(v);
15135             var ps = v.split('.');
15136             var whole = ps[0];
15137             
15138             var r = /(\d+)(\d{3})/;
15139             // add comma's
15140             
15141             if(thousandsDelimiter.length != 0) {
15142                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15143             } 
15144             
15145             var sub = ps[1] ?
15146                     // has decimals..
15147                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15148                     // does not have decimals
15149                     (decimals ? ('.' + zero) : '');
15150             
15151             
15152             return whole + sub ;
15153         },
15154         
15155         /**
15156          * Parse a value into a formatted date using the specified format pattern.
15157          * @param {Mixed} value The value to format
15158          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15159          * @return {String} The formatted date string
15160          */
15161         date : function(v, format){
15162             if(!v){
15163                 return "";
15164             }
15165             if(!(v instanceof Date)){
15166                 v = new Date(Date.parse(v));
15167             }
15168             return v.dateFormat(format || Roo.util.Format.defaults.date);
15169         },
15170
15171         /**
15172          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15173          * @param {String} format Any valid date format string
15174          * @return {Function} The date formatting function
15175          */
15176         dateRenderer : function(format){
15177             return function(v){
15178                 return Roo.util.Format.date(v, format);  
15179             };
15180         },
15181
15182         // private
15183         stripTagsRE : /<\/?[^>]+>/gi,
15184         
15185         /**
15186          * Strips all HTML tags
15187          * @param {Mixed} value The text from which to strip tags
15188          * @return {String} The stripped text
15189          */
15190         stripTags : function(v){
15191             return !v ? v : String(v).replace(this.stripTagsRE, "");
15192         },
15193         
15194         /**
15195          * Size in Mb,Gb etc.
15196          * @param {Number} value The number to be formated
15197          * @param {number} decimals how many decimal places
15198          * @return {String} the formated string
15199          */
15200         size : function(value, decimals)
15201         {
15202             var sizes = ['b', 'k', 'M', 'G', 'T'];
15203             if (value == 0) {
15204                 return 0;
15205             }
15206             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15207             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15208         }
15209         
15210         
15211         
15212     };
15213 }();
15214 Roo.util.Format.defaults = {
15215     date : 'd/M/Y'
15216 };/*
15217  * Based on:
15218  * Ext JS Library 1.1.1
15219  * Copyright(c) 2006-2007, Ext JS, LLC.
15220  *
15221  * Originally Released Under LGPL - original licence link has changed is not relivant.
15222  *
15223  * Fork - LGPL
15224  * <script type="text/javascript">
15225  */
15226
15227
15228  
15229
15230 /**
15231  * @class Roo.MasterTemplate
15232  * @extends Roo.Template
15233  * Provides a template that can have child templates. The syntax is:
15234 <pre><code>
15235 var t = new Roo.MasterTemplate(
15236         '&lt;select name="{name}"&gt;',
15237                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15238         '&lt;/select&gt;'
15239 );
15240 t.add('options', {value: 'foo', text: 'bar'});
15241 // or you can add multiple child elements in one shot
15242 t.addAll('options', [
15243     {value: 'foo', text: 'bar'},
15244     {value: 'foo2', text: 'bar2'},
15245     {value: 'foo3', text: 'bar3'}
15246 ]);
15247 // then append, applying the master template values
15248 t.append('my-form', {name: 'my-select'});
15249 </code></pre>
15250 * A name attribute for the child template is not required if you have only one child
15251 * template or you want to refer to them by index.
15252  */
15253 Roo.MasterTemplate = function(){
15254     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15255     this.originalHtml = this.html;
15256     var st = {};
15257     var m, re = this.subTemplateRe;
15258     re.lastIndex = 0;
15259     var subIndex = 0;
15260     while(m = re.exec(this.html)){
15261         var name = m[1], content = m[2];
15262         st[subIndex] = {
15263             name: name,
15264             index: subIndex,
15265             buffer: [],
15266             tpl : new Roo.Template(content)
15267         };
15268         if(name){
15269             st[name] = st[subIndex];
15270         }
15271         st[subIndex].tpl.compile();
15272         st[subIndex].tpl.call = this.call.createDelegate(this);
15273         subIndex++;
15274     }
15275     this.subCount = subIndex;
15276     this.subs = st;
15277 };
15278 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15279     /**
15280     * The regular expression used to match sub templates
15281     * @type RegExp
15282     * @property
15283     */
15284     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15285
15286     /**
15287      * Applies the passed values to a child template.
15288      * @param {String/Number} name (optional) The name or index of the child template
15289      * @param {Array/Object} values The values to be applied to the template
15290      * @return {MasterTemplate} this
15291      */
15292      add : function(name, values){
15293         if(arguments.length == 1){
15294             values = arguments[0];
15295             name = 0;
15296         }
15297         var s = this.subs[name];
15298         s.buffer[s.buffer.length] = s.tpl.apply(values);
15299         return this;
15300     },
15301
15302     /**
15303      * Applies all the passed values to a child template.
15304      * @param {String/Number} name (optional) The name or index of the child template
15305      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15306      * @param {Boolean} reset (optional) True to reset the template first
15307      * @return {MasterTemplate} this
15308      */
15309     fill : function(name, values, reset){
15310         var a = arguments;
15311         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15312             values = a[0];
15313             name = 0;
15314             reset = a[1];
15315         }
15316         if(reset){
15317             this.reset();
15318         }
15319         for(var i = 0, len = values.length; i < len; i++){
15320             this.add(name, values[i]);
15321         }
15322         return this;
15323     },
15324
15325     /**
15326      * Resets the template for reuse
15327      * @return {MasterTemplate} this
15328      */
15329      reset : function(){
15330         var s = this.subs;
15331         for(var i = 0; i < this.subCount; i++){
15332             s[i].buffer = [];
15333         }
15334         return this;
15335     },
15336
15337     applyTemplate : function(values){
15338         var s = this.subs;
15339         var replaceIndex = -1;
15340         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15341             return s[++replaceIndex].buffer.join("");
15342         });
15343         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15344     },
15345
15346     apply : function(){
15347         return this.applyTemplate.apply(this, arguments);
15348     },
15349
15350     compile : function(){return this;}
15351 });
15352
15353 /**
15354  * Alias for fill().
15355  * @method
15356  */
15357 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15358  /**
15359  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15360  * var tpl = Roo.MasterTemplate.from('element-id');
15361  * @param {String/HTMLElement} el
15362  * @param {Object} config
15363  * @static
15364  */
15365 Roo.MasterTemplate.from = function(el, config){
15366     el = Roo.getDom(el);
15367     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15368 };/*
15369  * Based on:
15370  * Ext JS Library 1.1.1
15371  * Copyright(c) 2006-2007, Ext JS, LLC.
15372  *
15373  * Originally Released Under LGPL - original licence link has changed is not relivant.
15374  *
15375  * Fork - LGPL
15376  * <script type="text/javascript">
15377  */
15378
15379  
15380 /**
15381  * @class Roo.util.CSS
15382  * Utility class for manipulating CSS rules
15383  * @static
15384
15385  */
15386 Roo.util.CSS = function(){
15387         var rules = null;
15388         var doc = document;
15389
15390     var camelRe = /(-[a-z])/gi;
15391     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15392
15393    return {
15394    /**
15395     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15396     * tag and appended to the HEAD of the document.
15397     * @param {String|Object} cssText The text containing the css rules
15398     * @param {String} id An id to add to the stylesheet for later removal
15399     * @return {StyleSheet}
15400     */
15401     createStyleSheet : function(cssText, id){
15402         var ss;
15403         var head = doc.getElementsByTagName("head")[0];
15404         var nrules = doc.createElement("style");
15405         nrules.setAttribute("type", "text/css");
15406         if(id){
15407             nrules.setAttribute("id", id);
15408         }
15409         if (typeof(cssText) != 'string') {
15410             // support object maps..
15411             // not sure if this a good idea.. 
15412             // perhaps it should be merged with the general css handling
15413             // and handle js style props.
15414             var cssTextNew = [];
15415             for(var n in cssText) {
15416                 var citems = [];
15417                 for(var k in cssText[n]) {
15418                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15419                 }
15420                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15421                 
15422             }
15423             cssText = cssTextNew.join("\n");
15424             
15425         }
15426        
15427        
15428        if(Roo.isIE){
15429            head.appendChild(nrules);
15430            ss = nrules.styleSheet;
15431            ss.cssText = cssText;
15432        }else{
15433            try{
15434                 nrules.appendChild(doc.createTextNode(cssText));
15435            }catch(e){
15436                nrules.cssText = cssText; 
15437            }
15438            head.appendChild(nrules);
15439            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15440        }
15441        this.cacheStyleSheet(ss);
15442        return ss;
15443    },
15444
15445    /**
15446     * Removes a style or link tag by id
15447     * @param {String} id The id of the tag
15448     */
15449    removeStyleSheet : function(id){
15450        var existing = doc.getElementById(id);
15451        if(existing){
15452            existing.parentNode.removeChild(existing);
15453        }
15454    },
15455
15456    /**
15457     * Dynamically swaps an existing stylesheet reference for a new one
15458     * @param {String} id The id of an existing link tag to remove
15459     * @param {String} url The href of the new stylesheet to include
15460     */
15461    swapStyleSheet : function(id, url){
15462        this.removeStyleSheet(id);
15463        var ss = doc.createElement("link");
15464        ss.setAttribute("rel", "stylesheet");
15465        ss.setAttribute("type", "text/css");
15466        ss.setAttribute("id", id);
15467        ss.setAttribute("href", url);
15468        doc.getElementsByTagName("head")[0].appendChild(ss);
15469    },
15470    
15471    /**
15472     * Refresh the rule cache if you have dynamically added stylesheets
15473     * @return {Object} An object (hash) of rules indexed by selector
15474     */
15475    refreshCache : function(){
15476        return this.getRules(true);
15477    },
15478
15479    // private
15480    cacheStyleSheet : function(stylesheet){
15481        if(!rules){
15482            rules = {};
15483        }
15484        try{// try catch for cross domain access issue
15485            var ssRules = stylesheet.cssRules || stylesheet.rules;
15486            for(var j = ssRules.length-1; j >= 0; --j){
15487                rules[ssRules[j].selectorText] = ssRules[j];
15488            }
15489        }catch(e){}
15490    },
15491    
15492    /**
15493     * Gets all css rules for the document
15494     * @param {Boolean} refreshCache true to refresh the internal cache
15495     * @return {Object} An object (hash) of rules indexed by selector
15496     */
15497    getRules : function(refreshCache){
15498                 if(rules == null || refreshCache){
15499                         rules = {};
15500                         var ds = doc.styleSheets;
15501                         for(var i =0, len = ds.length; i < len; i++){
15502                             try{
15503                         this.cacheStyleSheet(ds[i]);
15504                     }catch(e){} 
15505                 }
15506                 }
15507                 return rules;
15508         },
15509         
15510         /**
15511     * Gets an an individual CSS rule by selector(s)
15512     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15513     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15514     * @return {CSSRule} The CSS rule or null if one is not found
15515     */
15516    getRule : function(selector, refreshCache){
15517                 var rs = this.getRules(refreshCache);
15518                 if(!(selector instanceof Array)){
15519                     return rs[selector];
15520                 }
15521                 for(var i = 0; i < selector.length; i++){
15522                         if(rs[selector[i]]){
15523                                 return rs[selector[i]];
15524                         }
15525                 }
15526                 return null;
15527         },
15528         
15529         
15530         /**
15531     * Updates a rule property
15532     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15533     * @param {String} property The css property
15534     * @param {String} value The new value for the property
15535     * @return {Boolean} true If a rule was found and updated
15536     */
15537    updateRule : function(selector, property, value){
15538                 if(!(selector instanceof Array)){
15539                         var rule = this.getRule(selector);
15540                         if(rule){
15541                                 rule.style[property.replace(camelRe, camelFn)] = value;
15542                                 return true;
15543                         }
15544                 }else{
15545                         for(var i = 0; i < selector.length; i++){
15546                                 if(this.updateRule(selector[i], property, value)){
15547                                         return true;
15548                                 }
15549                         }
15550                 }
15551                 return false;
15552         }
15553    };   
15554 }();/*
15555  * Based on:
15556  * Ext JS Library 1.1.1
15557  * Copyright(c) 2006-2007, Ext JS, LLC.
15558  *
15559  * Originally Released Under LGPL - original licence link has changed is not relivant.
15560  *
15561  * Fork - LGPL
15562  * <script type="text/javascript">
15563  */
15564
15565  
15566
15567 /**
15568  * @class Roo.util.ClickRepeater
15569  * @extends Roo.util.Observable
15570  * 
15571  * A wrapper class which can be applied to any element. Fires a "click" event while the
15572  * mouse is pressed. The interval between firings may be specified in the config but
15573  * defaults to 10 milliseconds.
15574  * 
15575  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15576  * 
15577  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15578  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15579  * Similar to an autorepeat key delay.
15580  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15581  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15582  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15583  *           "interval" and "delay" are ignored. "immediate" is honored.
15584  * @cfg {Boolean} preventDefault True to prevent the default click event
15585  * @cfg {Boolean} stopDefault True to stop the default click event
15586  * 
15587  * @history
15588  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15589  *     2007-02-02 jvs Renamed to ClickRepeater
15590  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15591  *
15592  *  @constructor
15593  * @param {String/HTMLElement/Element} el The element to listen on
15594  * @param {Object} config
15595  **/
15596 Roo.util.ClickRepeater = function(el, config)
15597 {
15598     this.el = Roo.get(el);
15599     this.el.unselectable();
15600
15601     Roo.apply(this, config);
15602
15603     this.addEvents({
15604     /**
15605      * @event mousedown
15606      * Fires when the mouse button is depressed.
15607      * @param {Roo.util.ClickRepeater} this
15608      */
15609         "mousedown" : true,
15610     /**
15611      * @event click
15612      * Fires on a specified interval during the time the element is pressed.
15613      * @param {Roo.util.ClickRepeater} this
15614      */
15615         "click" : true,
15616     /**
15617      * @event mouseup
15618      * Fires when the mouse key is released.
15619      * @param {Roo.util.ClickRepeater} this
15620      */
15621         "mouseup" : true
15622     });
15623
15624     this.el.on("mousedown", this.handleMouseDown, this);
15625     if(this.preventDefault || this.stopDefault){
15626         this.el.on("click", function(e){
15627             if(this.preventDefault){
15628                 e.preventDefault();
15629             }
15630             if(this.stopDefault){
15631                 e.stopEvent();
15632             }
15633         }, this);
15634     }
15635
15636     // allow inline handler
15637     if(this.handler){
15638         this.on("click", this.handler,  this.scope || this);
15639     }
15640
15641     Roo.util.ClickRepeater.superclass.constructor.call(this);
15642 };
15643
15644 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15645     interval : 20,
15646     delay: 250,
15647     preventDefault : true,
15648     stopDefault : false,
15649     timer : 0,
15650
15651     // private
15652     handleMouseDown : function(){
15653         clearTimeout(this.timer);
15654         this.el.blur();
15655         if(this.pressClass){
15656             this.el.addClass(this.pressClass);
15657         }
15658         this.mousedownTime = new Date();
15659
15660         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15661         this.el.on("mouseout", this.handleMouseOut, this);
15662
15663         this.fireEvent("mousedown", this);
15664         this.fireEvent("click", this);
15665         
15666         this.timer = this.click.defer(this.delay || this.interval, this);
15667     },
15668
15669     // private
15670     click : function(){
15671         this.fireEvent("click", this);
15672         this.timer = this.click.defer(this.getInterval(), this);
15673     },
15674
15675     // private
15676     getInterval: function(){
15677         if(!this.accelerate){
15678             return this.interval;
15679         }
15680         var pressTime = this.mousedownTime.getElapsed();
15681         if(pressTime < 500){
15682             return 400;
15683         }else if(pressTime < 1700){
15684             return 320;
15685         }else if(pressTime < 2600){
15686             return 250;
15687         }else if(pressTime < 3500){
15688             return 180;
15689         }else if(pressTime < 4400){
15690             return 140;
15691         }else if(pressTime < 5300){
15692             return 80;
15693         }else if(pressTime < 6200){
15694             return 50;
15695         }else{
15696             return 10;
15697         }
15698     },
15699
15700     // private
15701     handleMouseOut : function(){
15702         clearTimeout(this.timer);
15703         if(this.pressClass){
15704             this.el.removeClass(this.pressClass);
15705         }
15706         this.el.on("mouseover", this.handleMouseReturn, this);
15707     },
15708
15709     // private
15710     handleMouseReturn : function(){
15711         this.el.un("mouseover", this.handleMouseReturn);
15712         if(this.pressClass){
15713             this.el.addClass(this.pressClass);
15714         }
15715         this.click();
15716     },
15717
15718     // private
15719     handleMouseUp : function(){
15720         clearTimeout(this.timer);
15721         this.el.un("mouseover", this.handleMouseReturn);
15722         this.el.un("mouseout", this.handleMouseOut);
15723         Roo.get(document).un("mouseup", this.handleMouseUp);
15724         this.el.removeClass(this.pressClass);
15725         this.fireEvent("mouseup", this);
15726     }
15727 });/**
15728  * @class Roo.util.Clipboard
15729  * @static
15730  * 
15731  * Clipboard UTILS
15732  * 
15733  **/
15734 Roo.util.Clipboard = {
15735     /**
15736      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15737      * @param {String} text to copy to clipboard
15738      */
15739     write : function(text) {
15740         // navigator clipboard api needs a secure context (https)
15741         if (navigator.clipboard && window.isSecureContext) {
15742             // navigator clipboard api method'
15743             navigator.clipboard.writeText(text);
15744             return ;
15745         } 
15746         // text area method
15747         var ta = document.createElement("textarea");
15748         ta.value = text;
15749         // make the textarea out of viewport
15750         ta.style.position = "fixed";
15751         ta.style.left = "-999999px";
15752         ta.style.top = "-999999px";
15753         document.body.appendChild(ta);
15754         ta.focus();
15755         ta.select();
15756         document.execCommand('copy');
15757         (function() {
15758             ta.remove();
15759         }).defer(100);
15760         
15761     }
15762         
15763 }
15764     /*
15765  * Based on:
15766  * Ext JS Library 1.1.1
15767  * Copyright(c) 2006-2007, Ext JS, LLC.
15768  *
15769  * Originally Released Under LGPL - original licence link has changed is not relivant.
15770  *
15771  * Fork - LGPL
15772  * <script type="text/javascript">
15773  */
15774
15775  
15776 /**
15777  * @class Roo.KeyNav
15778  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15779  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15780  * way to implement custom navigation schemes for any UI component.</p>
15781  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15782  * pageUp, pageDown, del, home, end.  Usage:</p>
15783  <pre><code>
15784 var nav = new Roo.KeyNav("my-element", {
15785     "left" : function(e){
15786         this.moveLeft(e.ctrlKey);
15787     },
15788     "right" : function(e){
15789         this.moveRight(e.ctrlKey);
15790     },
15791     "enter" : function(e){
15792         this.save();
15793     },
15794     scope : this
15795 });
15796 </code></pre>
15797  * @constructor
15798  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15799  * @param {Object} config The config
15800  */
15801 Roo.KeyNav = function(el, config){
15802     this.el = Roo.get(el);
15803     Roo.apply(this, config);
15804     if(!this.disabled){
15805         this.disabled = true;
15806         this.enable();
15807     }
15808 };
15809
15810 Roo.KeyNav.prototype = {
15811     /**
15812      * @cfg {Boolean} disabled
15813      * True to disable this KeyNav instance (defaults to false)
15814      */
15815     disabled : false,
15816     /**
15817      * @cfg {String} defaultEventAction
15818      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15819      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15820      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15821      */
15822     defaultEventAction: "stopEvent",
15823     /**
15824      * @cfg {Boolean} forceKeyDown
15825      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15826      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15827      * handle keydown instead of keypress.
15828      */
15829     forceKeyDown : false,
15830
15831     // private
15832     prepareEvent : function(e){
15833         var k = e.getKey();
15834         var h = this.keyToHandler[k];
15835         //if(h && this[h]){
15836         //    e.stopPropagation();
15837         //}
15838         if(Roo.isSafari && h && k >= 37 && k <= 40){
15839             e.stopEvent();
15840         }
15841     },
15842
15843     // private
15844     relay : function(e){
15845         var k = e.getKey();
15846         var h = this.keyToHandler[k];
15847         if(h && this[h]){
15848             if(this.doRelay(e, this[h], h) !== true){
15849                 e[this.defaultEventAction]();
15850             }
15851         }
15852     },
15853
15854     // private
15855     doRelay : function(e, h, hname){
15856         return h.call(this.scope || this, e);
15857     },
15858
15859     // possible handlers
15860     enter : false,
15861     left : false,
15862     right : false,
15863     up : false,
15864     down : false,
15865     tab : false,
15866     esc : false,
15867     pageUp : false,
15868     pageDown : false,
15869     del : false,
15870     home : false,
15871     end : false,
15872
15873     // quick lookup hash
15874     keyToHandler : {
15875         37 : "left",
15876         39 : "right",
15877         38 : "up",
15878         40 : "down",
15879         33 : "pageUp",
15880         34 : "pageDown",
15881         46 : "del",
15882         36 : "home",
15883         35 : "end",
15884         13 : "enter",
15885         27 : "esc",
15886         9  : "tab"
15887     },
15888
15889         /**
15890          * Enable this KeyNav
15891          */
15892         enable: function(){
15893                 if(this.disabled){
15894             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15895             // the EventObject will normalize Safari automatically
15896             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15897                 this.el.on("keydown", this.relay,  this);
15898             }else{
15899                 this.el.on("keydown", this.prepareEvent,  this);
15900                 this.el.on("keypress", this.relay,  this);
15901             }
15902                     this.disabled = false;
15903                 }
15904         },
15905
15906         /**
15907          * Disable this KeyNav
15908          */
15909         disable: function(){
15910                 if(!this.disabled){
15911                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15912                 this.el.un("keydown", this.relay);
15913             }else{
15914                 this.el.un("keydown", this.prepareEvent);
15915                 this.el.un("keypress", this.relay);
15916             }
15917                     this.disabled = true;
15918                 }
15919         }
15920 };/*
15921  * Based on:
15922  * Ext JS Library 1.1.1
15923  * Copyright(c) 2006-2007, Ext JS, LLC.
15924  *
15925  * Originally Released Under LGPL - original licence link has changed is not relivant.
15926  *
15927  * Fork - LGPL
15928  * <script type="text/javascript">
15929  */
15930
15931  
15932 /**
15933  * @class Roo.KeyMap
15934  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15935  * The constructor accepts the same config object as defined by {@link #addBinding}.
15936  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15937  * combination it will call the function with this signature (if the match is a multi-key
15938  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15939  * A KeyMap can also handle a string representation of keys.<br />
15940  * Usage:
15941  <pre><code>
15942 // map one key by key code
15943 var map = new Roo.KeyMap("my-element", {
15944     key: 13, // or Roo.EventObject.ENTER
15945     fn: myHandler,
15946     scope: myObject
15947 });
15948
15949 // map multiple keys to one action by string
15950 var map = new Roo.KeyMap("my-element", {
15951     key: "a\r\n\t",
15952     fn: myHandler,
15953     scope: myObject
15954 });
15955
15956 // map multiple keys to multiple actions by strings and array of codes
15957 var map = new Roo.KeyMap("my-element", [
15958     {
15959         key: [10,13],
15960         fn: function(){ alert("Return was pressed"); }
15961     }, {
15962         key: "abc",
15963         fn: function(){ alert('a, b or c was pressed'); }
15964     }, {
15965         key: "\t",
15966         ctrl:true,
15967         shift:true,
15968         fn: function(){ alert('Control + shift + tab was pressed.'); }
15969     }
15970 ]);
15971 </code></pre>
15972  * <b>Note: A KeyMap starts enabled</b>
15973  * @constructor
15974  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15975  * @param {Object} config The config (see {@link #addBinding})
15976  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15977  */
15978 Roo.KeyMap = function(el, config, eventName){
15979     this.el  = Roo.get(el);
15980     this.eventName = eventName || "keydown";
15981     this.bindings = [];
15982     if(config){
15983         this.addBinding(config);
15984     }
15985     this.enable();
15986 };
15987
15988 Roo.KeyMap.prototype = {
15989     /**
15990      * True to stop the event from bubbling and prevent the default browser action if the
15991      * key was handled by the KeyMap (defaults to false)
15992      * @type Boolean
15993      */
15994     stopEvent : false,
15995
15996     /**
15997      * Add a new binding to this KeyMap. The following config object properties are supported:
15998      * <pre>
15999 Property    Type             Description
16000 ----------  ---------------  ----------------------------------------------------------------------
16001 key         String/Array     A single keycode or an array of keycodes to handle
16002 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16003 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16004 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16005 fn          Function         The function to call when KeyMap finds the expected key combination
16006 scope       Object           The scope of the callback function
16007 </pre>
16008      *
16009      * Usage:
16010      * <pre><code>
16011 // Create a KeyMap
16012 var map = new Roo.KeyMap(document, {
16013     key: Roo.EventObject.ENTER,
16014     fn: handleKey,
16015     scope: this
16016 });
16017
16018 //Add a new binding to the existing KeyMap later
16019 map.addBinding({
16020     key: 'abc',
16021     shift: true,
16022     fn: handleKey,
16023     scope: this
16024 });
16025 </code></pre>
16026      * @param {Object/Array} config A single KeyMap config or an array of configs
16027      */
16028         addBinding : function(config){
16029         if(config instanceof Array){
16030             for(var i = 0, len = config.length; i < len; i++){
16031                 this.addBinding(config[i]);
16032             }
16033             return;
16034         }
16035         var keyCode = config.key,
16036             shift = config.shift, 
16037             ctrl = config.ctrl, 
16038             alt = config.alt,
16039             fn = config.fn,
16040             scope = config.scope;
16041         if(typeof keyCode == "string"){
16042             var ks = [];
16043             var keyString = keyCode.toUpperCase();
16044             for(var j = 0, len = keyString.length; j < len; j++){
16045                 ks.push(keyString.charCodeAt(j));
16046             }
16047             keyCode = ks;
16048         }
16049         var keyArray = keyCode instanceof Array;
16050         var handler = function(e){
16051             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16052                 var k = e.getKey();
16053                 if(keyArray){
16054                     for(var i = 0, len = keyCode.length; i < len; i++){
16055                         if(keyCode[i] == k){
16056                           if(this.stopEvent){
16057                               e.stopEvent();
16058                           }
16059                           fn.call(scope || window, k, e);
16060                           return;
16061                         }
16062                     }
16063                 }else{
16064                     if(k == keyCode){
16065                         if(this.stopEvent){
16066                            e.stopEvent();
16067                         }
16068                         fn.call(scope || window, k, e);
16069                     }
16070                 }
16071             }
16072         };
16073         this.bindings.push(handler);  
16074         },
16075
16076     /**
16077      * Shorthand for adding a single key listener
16078      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16079      * following options:
16080      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16081      * @param {Function} fn The function to call
16082      * @param {Object} scope (optional) The scope of the function
16083      */
16084     on : function(key, fn, scope){
16085         var keyCode, shift, ctrl, alt;
16086         if(typeof key == "object" && !(key instanceof Array)){
16087             keyCode = key.key;
16088             shift = key.shift;
16089             ctrl = key.ctrl;
16090             alt = key.alt;
16091         }else{
16092             keyCode = key;
16093         }
16094         this.addBinding({
16095             key: keyCode,
16096             shift: shift,
16097             ctrl: ctrl,
16098             alt: alt,
16099             fn: fn,
16100             scope: scope
16101         })
16102     },
16103
16104     // private
16105     handleKeyDown : function(e){
16106             if(this.enabled){ //just in case
16107             var b = this.bindings;
16108             for(var i = 0, len = b.length; i < len; i++){
16109                 b[i].call(this, e);
16110             }
16111             }
16112         },
16113         
16114         /**
16115          * Returns true if this KeyMap is enabled
16116          * @return {Boolean} 
16117          */
16118         isEnabled : function(){
16119             return this.enabled;  
16120         },
16121         
16122         /**
16123          * Enables this KeyMap
16124          */
16125         enable: function(){
16126                 if(!this.enabled){
16127                     this.el.on(this.eventName, this.handleKeyDown, this);
16128                     this.enabled = true;
16129                 }
16130         },
16131
16132         /**
16133          * Disable this KeyMap
16134          */
16135         disable: function(){
16136                 if(this.enabled){
16137                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16138                     this.enabled = false;
16139                 }
16140         }
16141 };/*
16142  * Based on:
16143  * Ext JS Library 1.1.1
16144  * Copyright(c) 2006-2007, Ext JS, LLC.
16145  *
16146  * Originally Released Under LGPL - original licence link has changed is not relivant.
16147  *
16148  * Fork - LGPL
16149  * <script type="text/javascript">
16150  */
16151
16152  
16153 /**
16154  * @class Roo.util.TextMetrics
16155  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16156  * wide, in pixels, a given block of text will be.
16157  * @static
16158  */
16159 Roo.util.TextMetrics = function(){
16160     var shared;
16161     return {
16162         /**
16163          * Measures the size of the specified text
16164          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16165          * that can affect the size of the rendered text
16166          * @param {String} text The text to measure
16167          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16168          * in order to accurately measure the text height
16169          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16170          */
16171         measure : function(el, text, fixedWidth){
16172             if(!shared){
16173                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16174             }
16175             shared.bind(el);
16176             shared.setFixedWidth(fixedWidth || 'auto');
16177             return shared.getSize(text);
16178         },
16179
16180         /**
16181          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16182          * the overhead of multiple calls to initialize the style properties on each measurement.
16183          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16184          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16185          * in order to accurately measure the text height
16186          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16187          */
16188         createInstance : function(el, fixedWidth){
16189             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16190         }
16191     };
16192 }();
16193
16194 /**
16195  * @class Roo.util.TextMetrics.Instance
16196  * Instance of  TextMetrics Calcuation
16197  * @constructor
16198  * Create a new TextMetrics Instance
16199  * @param {Object} bindto
16200  * @param {Boolean} fixedWidth
16201  */
16202
16203 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16204 {
16205     var ml = new Roo.Element(document.createElement('div'));
16206     document.body.appendChild(ml.dom);
16207     ml.position('absolute');
16208     ml.setLeftTop(-1000, -1000);
16209     ml.hide();
16210
16211     if(fixedWidth){
16212         ml.setWidth(fixedWidth);
16213     }
16214      
16215     var instance = {
16216         /**
16217          * Returns the size of the specified text based on the internal element's style and width properties
16218          * @param {String} text The text to measure
16219          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16220          */
16221         getSize : function(text){
16222             ml.update(text);
16223             var s = ml.getSize();
16224             ml.update('');
16225             return s;
16226         },
16227
16228         /**
16229          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16230          * that can affect the size of the rendered text
16231          * @param {String/HTMLElement} el The element, dom node or id
16232          */
16233         bind : function(el){
16234             ml.setStyle(
16235                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16236             );
16237         },
16238
16239         /**
16240          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16241          * to set a fixed width in order to accurately measure the text height.
16242          * @param {Number} width The width to set on the element
16243          */
16244         setFixedWidth : function(width){
16245             ml.setWidth(width);
16246         },
16247
16248         /**
16249          * Returns the measured width of the specified text
16250          * @param {String} text The text to measure
16251          * @return {Number} width The width in pixels
16252          */
16253         getWidth : function(text){
16254             ml.dom.style.width = 'auto';
16255             return this.getSize(text).width;
16256         },
16257
16258         /**
16259          * Returns the measured height of the specified text.  For multiline text, be sure to call
16260          * {@link #setFixedWidth} if necessary.
16261          * @param {String} text The text to measure
16262          * @return {Number} height The height in pixels
16263          */
16264         getHeight : function(text){
16265             return this.getSize(text).height;
16266         }
16267     };
16268
16269     instance.bind(bindTo);
16270
16271     return instance;
16272 };
16273
16274 // backwards compat
16275 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16276  * Based on:
16277  * Ext JS Library 1.1.1
16278  * Copyright(c) 2006-2007, Ext JS, LLC.
16279  *
16280  * Originally Released Under LGPL - original licence link has changed is not relivant.
16281  *
16282  * Fork - LGPL
16283  * <script type="text/javascript">
16284  */
16285
16286 /**
16287  * @class Roo.state.Provider
16288  * Abstract base class for state provider implementations. This class provides methods
16289  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16290  * Provider interface.
16291  */
16292 Roo.state.Provider = function(){
16293     /**
16294      * @event statechange
16295      * Fires when a state change occurs.
16296      * @param {Provider} this This state provider
16297      * @param {String} key The state key which was changed
16298      * @param {String} value The encoded value for the state
16299      */
16300     this.addEvents({
16301         "statechange": true
16302     });
16303     this.state = {};
16304     Roo.state.Provider.superclass.constructor.call(this);
16305 };
16306 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16307     /**
16308      * Returns the current value for a key
16309      * @param {String} name The key name
16310      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16311      * @return {Mixed} The state data
16312      */
16313     get : function(name, defaultValue){
16314         return typeof this.state[name] == "undefined" ?
16315             defaultValue : this.state[name];
16316     },
16317     
16318     /**
16319      * Clears a value from the state
16320      * @param {String} name The key name
16321      */
16322     clear : function(name){
16323         delete this.state[name];
16324         this.fireEvent("statechange", this, name, null);
16325     },
16326     
16327     /**
16328      * Sets the value for a key
16329      * @param {String} name The key name
16330      * @param {Mixed} value The value to set
16331      */
16332     set : function(name, value){
16333         this.state[name] = value;
16334         this.fireEvent("statechange", this, name, value);
16335     },
16336     
16337     /**
16338      * Decodes a string previously encoded with {@link #encodeValue}.
16339      * @param {String} value The value to decode
16340      * @return {Mixed} The decoded value
16341      */
16342     decodeValue : function(cookie){
16343         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16344         var matches = re.exec(unescape(cookie));
16345         if(!matches || !matches[1]) {
16346             return; // non state cookie
16347         }
16348         var type = matches[1];
16349         var v = matches[2];
16350         switch(type){
16351             case "n":
16352                 return parseFloat(v);
16353             case "d":
16354                 return new Date(Date.parse(v));
16355             case "b":
16356                 return (v == "1");
16357             case "a":
16358                 var all = [];
16359                 var values = v.split("^");
16360                 for(var i = 0, len = values.length; i < len; i++){
16361                     all.push(this.decodeValue(values[i]));
16362                 }
16363                 return all;
16364            case "o":
16365                 var all = {};
16366                 var values = v.split("^");
16367                 for(var i = 0, len = values.length; i < len; i++){
16368                     var kv = values[i].split("=");
16369                     all[kv[0]] = this.decodeValue(kv[1]);
16370                 }
16371                 return all;
16372            default:
16373                 return v;
16374         }
16375     },
16376     
16377     /**
16378      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16379      * @param {Mixed} value The value to encode
16380      * @return {String} The encoded value
16381      */
16382     encodeValue : function(v){
16383         var enc;
16384         if(typeof v == "number"){
16385             enc = "n:" + v;
16386         }else if(typeof v == "boolean"){
16387             enc = "b:" + (v ? "1" : "0");
16388         }else if(v instanceof Date){
16389             enc = "d:" + v.toGMTString();
16390         }else if(v instanceof Array){
16391             var flat = "";
16392             for(var i = 0, len = v.length; i < len; i++){
16393                 flat += this.encodeValue(v[i]);
16394                 if(i != len-1) {
16395                     flat += "^";
16396                 }
16397             }
16398             enc = "a:" + flat;
16399         }else if(typeof v == "object"){
16400             var flat = "";
16401             for(var key in v){
16402                 if(typeof v[key] != "function"){
16403                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16404                 }
16405             }
16406             enc = "o:" + flat.substring(0, flat.length-1);
16407         }else{
16408             enc = "s:" + v;
16409         }
16410         return escape(enc);        
16411     }
16412 });
16413
16414 /*
16415  * Based on:
16416  * Ext JS Library 1.1.1
16417  * Copyright(c) 2006-2007, Ext JS, LLC.
16418  *
16419  * Originally Released Under LGPL - original licence link has changed is not relivant.
16420  *
16421  * Fork - LGPL
16422  * <script type="text/javascript">
16423  */
16424 /**
16425  * @class Roo.state.Manager
16426  * This is the global state manager. By default all components that are "state aware" check this class
16427  * for state information if you don't pass them a custom state provider. In order for this class
16428  * to be useful, it must be initialized with a provider when your application initializes.
16429  <pre><code>
16430 // in your initialization function
16431 init : function(){
16432    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16433    ...
16434    // supposed you have a {@link Roo.BorderLayout}
16435    var layout = new Roo.BorderLayout(...);
16436    layout.restoreState();
16437    // or a {Roo.BasicDialog}
16438    var dialog = new Roo.BasicDialog(...);
16439    dialog.restoreState();
16440  </code></pre>
16441  * @static
16442  */
16443 Roo.state.Manager = function(){
16444     var provider = new Roo.state.Provider();
16445     
16446     return {
16447         /**
16448          * Configures the default state provider for your application
16449          * @param {Provider} stateProvider The state provider to set
16450          */
16451         setProvider : function(stateProvider){
16452             provider = stateProvider;
16453         },
16454         
16455         /**
16456          * Returns the current value for a key
16457          * @param {String} name The key name
16458          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16459          * @return {Mixed} The state data
16460          */
16461         get : function(key, defaultValue){
16462             return provider.get(key, defaultValue);
16463         },
16464         
16465         /**
16466          * Sets the value for a key
16467          * @param {String} name The key name
16468          * @param {Mixed} value The state data
16469          */
16470          set : function(key, value){
16471             provider.set(key, value);
16472         },
16473         
16474         /**
16475          * Clears a value from the state
16476          * @param {String} name The key name
16477          */
16478         clear : function(key){
16479             provider.clear(key);
16480         },
16481         
16482         /**
16483          * Gets the currently configured state provider
16484          * @return {Provider} The state provider
16485          */
16486         getProvider : function(){
16487             return provider;
16488         }
16489     };
16490 }();
16491 /*
16492  * Based on:
16493  * Ext JS Library 1.1.1
16494  * Copyright(c) 2006-2007, Ext JS, LLC.
16495  *
16496  * Originally Released Under LGPL - original licence link has changed is not relivant.
16497  *
16498  * Fork - LGPL
16499  * <script type="text/javascript">
16500  */
16501 /**
16502  * @class Roo.state.CookieProvider
16503  * @extends Roo.state.Provider
16504  * The default Provider implementation which saves state via cookies.
16505  * <br />Usage:
16506  <pre><code>
16507    var cp = new Roo.state.CookieProvider({
16508        path: "/cgi-bin/",
16509        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16510        domain: "roojs.com"
16511    })
16512    Roo.state.Manager.setProvider(cp);
16513  </code></pre>
16514  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16515  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16516  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16517  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16518  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16519  * domain the page is running on including the 'www' like 'www.roojs.com')
16520  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16521  * @constructor
16522  * Create a new CookieProvider
16523  * @param {Object} config The configuration object
16524  */
16525 Roo.state.CookieProvider = function(config){
16526     Roo.state.CookieProvider.superclass.constructor.call(this);
16527     this.path = "/";
16528     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16529     this.domain = null;
16530     this.secure = false;
16531     Roo.apply(this, config);
16532     this.state = this.readCookies();
16533 };
16534
16535 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16536     // private
16537     set : function(name, value){
16538         if(typeof value == "undefined" || value === null){
16539             this.clear(name);
16540             return;
16541         }
16542         this.setCookie(name, value);
16543         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16544     },
16545
16546     // private
16547     clear : function(name){
16548         this.clearCookie(name);
16549         Roo.state.CookieProvider.superclass.clear.call(this, name);
16550     },
16551
16552     // private
16553     readCookies : function(){
16554         var cookies = {};
16555         var c = document.cookie + ";";
16556         var re = /\s?(.*?)=(.*?);/g;
16557         var matches;
16558         while((matches = re.exec(c)) != null){
16559             var name = matches[1];
16560             var value = matches[2];
16561             if(name && name.substring(0,3) == "ys-"){
16562                 cookies[name.substr(3)] = this.decodeValue(value);
16563             }
16564         }
16565         return cookies;
16566     },
16567
16568     // private
16569     setCookie : function(name, value){
16570         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16571            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16572            ((this.path == null) ? "" : ("; path=" + this.path)) +
16573            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16574            ((this.secure == true) ? "; secure" : "");
16575     },
16576
16577     // private
16578     clearCookie : function(name){
16579         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16580            ((this.path == null) ? "" : ("; path=" + this.path)) +
16581            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16582            ((this.secure == true) ? "; secure" : "");
16583     }
16584 });/*
16585  * Based on:
16586  * Ext JS Library 1.1.1
16587  * Copyright(c) 2006-2007, Ext JS, LLC.
16588  *
16589  * Originally Released Under LGPL - original licence link has changed is not relivant.
16590  *
16591  * Fork - LGPL
16592  * <script type="text/javascript">
16593  */
16594  
16595
16596 /**
16597  * @class Roo.ComponentMgr
16598  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16599  * @static
16600  */
16601 Roo.ComponentMgr = function(){
16602     var all = new Roo.util.MixedCollection();
16603
16604     return {
16605         /**
16606          * Registers a component.
16607          * @param {Roo.Component} c The component
16608          */
16609         register : function(c){
16610             all.add(c);
16611         },
16612
16613         /**
16614          * Unregisters a component.
16615          * @param {Roo.Component} c The component
16616          */
16617         unregister : function(c){
16618             all.remove(c);
16619         },
16620
16621         /**
16622          * Returns a component by id
16623          * @param {String} id The component id
16624          */
16625         get : function(id){
16626             return all.get(id);
16627         },
16628
16629         /**
16630          * Registers a function that will be called when a specified component is added to ComponentMgr
16631          * @param {String} id The component id
16632          * @param {Funtction} fn The callback function
16633          * @param {Object} scope The scope of the callback
16634          */
16635         onAvailable : function(id, fn, scope){
16636             all.on("add", function(index, o){
16637                 if(o.id == id){
16638                     fn.call(scope || o, o);
16639                     all.un("add", fn, scope);
16640                 }
16641             });
16642         }
16643     };
16644 }();/*
16645  * Based on:
16646  * Ext JS Library 1.1.1
16647  * Copyright(c) 2006-2007, Ext JS, LLC.
16648  *
16649  * Originally Released Under LGPL - original licence link has changed is not relivant.
16650  *
16651  * Fork - LGPL
16652  * <script type="text/javascript">
16653  */
16654  
16655 /**
16656  * @class Roo.Component
16657  * @extends Roo.util.Observable
16658  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16659  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16660  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16661  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16662  * All visual components (widgets) that require rendering into a layout should subclass Component.
16663  * @constructor
16664  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16665  * 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
16666  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16667  */
16668 Roo.Component = function(config){
16669     config = config || {};
16670     if(config.tagName || config.dom || typeof config == "string"){ // element object
16671         config = {el: config, id: config.id || config};
16672     }
16673     this.initialConfig = config;
16674
16675     Roo.apply(this, config);
16676     this.addEvents({
16677         /**
16678          * @event disable
16679          * Fires after the component is disabled.
16680              * @param {Roo.Component} this
16681              */
16682         disable : true,
16683         /**
16684          * @event enable
16685          * Fires after the component is enabled.
16686              * @param {Roo.Component} this
16687              */
16688         enable : true,
16689         /**
16690          * @event beforeshow
16691          * Fires before the component is shown.  Return false to stop the show.
16692              * @param {Roo.Component} this
16693              */
16694         beforeshow : true,
16695         /**
16696          * @event show
16697          * Fires after the component is shown.
16698              * @param {Roo.Component} this
16699              */
16700         show : true,
16701         /**
16702          * @event beforehide
16703          * Fires before the component is hidden. Return false to stop the hide.
16704              * @param {Roo.Component} this
16705              */
16706         beforehide : true,
16707         /**
16708          * @event hide
16709          * Fires after the component is hidden.
16710              * @param {Roo.Component} this
16711              */
16712         hide : true,
16713         /**
16714          * @event beforerender
16715          * Fires before the component is rendered. Return false to stop the render.
16716              * @param {Roo.Component} this
16717              */
16718         beforerender : true,
16719         /**
16720          * @event render
16721          * Fires after the component is rendered.
16722              * @param {Roo.Component} this
16723              */
16724         render : true,
16725         /**
16726          * @event beforedestroy
16727          * Fires before the component is destroyed. Return false to stop the destroy.
16728              * @param {Roo.Component} this
16729              */
16730         beforedestroy : true,
16731         /**
16732          * @event destroy
16733          * Fires after the component is destroyed.
16734              * @param {Roo.Component} this
16735              */
16736         destroy : true
16737     });
16738     if(!this.id){
16739         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16740     }
16741     Roo.ComponentMgr.register(this);
16742     Roo.Component.superclass.constructor.call(this);
16743     this.initComponent();
16744     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16745         this.render(this.renderTo);
16746         delete this.renderTo;
16747     }
16748 };
16749
16750 /** @private */
16751 Roo.Component.AUTO_ID = 1000;
16752
16753 Roo.extend(Roo.Component, Roo.util.Observable, {
16754     /**
16755      * @scope Roo.Component.prototype
16756      * @type {Boolean}
16757      * true if this component is hidden. Read-only.
16758      */
16759     hidden : false,
16760     /**
16761      * @type {Boolean}
16762      * true if this component is disabled. Read-only.
16763      */
16764     disabled : false,
16765     /**
16766      * @type {Boolean}
16767      * true if this component has been rendered. Read-only.
16768      */
16769     rendered : false,
16770     
16771     /** @cfg {String} disableClass
16772      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16773      */
16774     disabledClass : "x-item-disabled",
16775         /** @cfg {Boolean} allowDomMove
16776          * Whether the component can move the Dom node when rendering (defaults to true).
16777          */
16778     allowDomMove : true,
16779     /** @cfg {String} hideMode (display|visibility)
16780      * How this component should hidden. Supported values are
16781      * "visibility" (css visibility), "offsets" (negative offset position) and
16782      * "display" (css display) - defaults to "display".
16783      */
16784     hideMode: 'display',
16785
16786     /** @private */
16787     ctype : "Roo.Component",
16788
16789     /**
16790      * @cfg {String} actionMode 
16791      * which property holds the element that used for  hide() / show() / disable() / enable()
16792      * default is 'el' for forms you probably want to set this to fieldEl 
16793      */
16794     actionMode : "el",
16795
16796     /** @private */
16797     getActionEl : function(){
16798         return this[this.actionMode];
16799     },
16800
16801     initComponent : Roo.emptyFn,
16802     /**
16803      * If this is a lazy rendering component, render it to its container element.
16804      * @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.
16805      */
16806     render : function(container, position){
16807         
16808         if(this.rendered){
16809             return this;
16810         }
16811         
16812         if(this.fireEvent("beforerender", this) === false){
16813             return false;
16814         }
16815         
16816         if(!container && this.el){
16817             this.el = Roo.get(this.el);
16818             container = this.el.dom.parentNode;
16819             this.allowDomMove = false;
16820         }
16821         this.container = Roo.get(container);
16822         this.rendered = true;
16823         if(position !== undefined){
16824             if(typeof position == 'number'){
16825                 position = this.container.dom.childNodes[position];
16826             }else{
16827                 position = Roo.getDom(position);
16828             }
16829         }
16830         this.onRender(this.container, position || null);
16831         if(this.cls){
16832             this.el.addClass(this.cls);
16833             delete this.cls;
16834         }
16835         if(this.style){
16836             this.el.applyStyles(this.style);
16837             delete this.style;
16838         }
16839         this.fireEvent("render", this);
16840         this.afterRender(this.container);
16841         if(this.hidden){
16842             this.hide();
16843         }
16844         if(this.disabled){
16845             this.disable();
16846         }
16847
16848         return this;
16849         
16850     },
16851
16852     /** @private */
16853     // default function is not really useful
16854     onRender : function(ct, position){
16855         if(this.el){
16856             this.el = Roo.get(this.el);
16857             if(this.allowDomMove !== false){
16858                 ct.dom.insertBefore(this.el.dom, position);
16859             }
16860         }
16861     },
16862
16863     /** @private */
16864     getAutoCreate : function(){
16865         var cfg = typeof this.autoCreate == "object" ?
16866                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16867         if(this.id && !cfg.id){
16868             cfg.id = this.id;
16869         }
16870         return cfg;
16871     },
16872
16873     /** @private */
16874     afterRender : Roo.emptyFn,
16875
16876     /**
16877      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16878      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16879      */
16880     destroy : function(){
16881         if(this.fireEvent("beforedestroy", this) !== false){
16882             this.purgeListeners();
16883             this.beforeDestroy();
16884             if(this.rendered){
16885                 this.el.removeAllListeners();
16886                 this.el.remove();
16887                 if(this.actionMode == "container"){
16888                     this.container.remove();
16889                 }
16890             }
16891             this.onDestroy();
16892             Roo.ComponentMgr.unregister(this);
16893             this.fireEvent("destroy", this);
16894         }
16895     },
16896
16897         /** @private */
16898     beforeDestroy : function(){
16899
16900     },
16901
16902         /** @private */
16903         onDestroy : function(){
16904
16905     },
16906
16907     /**
16908      * Returns the underlying {@link Roo.Element}.
16909      * @return {Roo.Element} The element
16910      */
16911     getEl : function(){
16912         return this.el;
16913     },
16914
16915     /**
16916      * Returns the id of this component.
16917      * @return {String}
16918      */
16919     getId : function(){
16920         return this.id;
16921     },
16922
16923     /**
16924      * Try to focus this component.
16925      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16926      * @return {Roo.Component} this
16927      */
16928     focus : function(selectText){
16929         if(this.rendered){
16930             this.el.focus();
16931             if(selectText === true){
16932                 this.el.dom.select();
16933             }
16934         }
16935         return this;
16936     },
16937
16938     /** @private */
16939     blur : function(){
16940         if(this.rendered){
16941             this.el.blur();
16942         }
16943         return this;
16944     },
16945
16946     /**
16947      * Disable this component.
16948      * @return {Roo.Component} this
16949      */
16950     disable : function(){
16951         if(this.rendered){
16952             this.onDisable();
16953         }
16954         this.disabled = true;
16955         this.fireEvent("disable", this);
16956         return this;
16957     },
16958
16959         // private
16960     onDisable : function(){
16961         this.getActionEl().addClass(this.disabledClass);
16962         this.el.dom.disabled = true;
16963     },
16964
16965     /**
16966      * Enable this component.
16967      * @return {Roo.Component} this
16968      */
16969     enable : function(){
16970         if(this.rendered){
16971             this.onEnable();
16972         }
16973         this.disabled = false;
16974         this.fireEvent("enable", this);
16975         return this;
16976     },
16977
16978         // private
16979     onEnable : function(){
16980         this.getActionEl().removeClass(this.disabledClass);
16981         this.el.dom.disabled = false;
16982     },
16983
16984     /**
16985      * Convenience function for setting disabled/enabled by boolean.
16986      * @param {Boolean} disabled
16987      */
16988     setDisabled : function(disabled){
16989         this[disabled ? "disable" : "enable"]();
16990     },
16991
16992     /**
16993      * Show this component.
16994      * @return {Roo.Component} this
16995      */
16996     show: function(){
16997         if(this.fireEvent("beforeshow", this) !== false){
16998             this.hidden = false;
16999             if(this.rendered){
17000                 this.onShow();
17001             }
17002             this.fireEvent("show", this);
17003         }
17004         return this;
17005     },
17006
17007     // private
17008     onShow : function(){
17009         var ae = this.getActionEl();
17010         if(this.hideMode == 'visibility'){
17011             ae.dom.style.visibility = "visible";
17012         }else if(this.hideMode == 'offsets'){
17013             ae.removeClass('x-hidden');
17014         }else{
17015             ae.dom.style.display = "";
17016         }
17017     },
17018
17019     /**
17020      * Hide this component.
17021      * @return {Roo.Component} this
17022      */
17023     hide: function(){
17024         if(this.fireEvent("beforehide", this) !== false){
17025             this.hidden = true;
17026             if(this.rendered){
17027                 this.onHide();
17028             }
17029             this.fireEvent("hide", this);
17030         }
17031         return this;
17032     },
17033
17034     // private
17035     onHide : function(){
17036         var ae = this.getActionEl();
17037         if(this.hideMode == 'visibility'){
17038             ae.dom.style.visibility = "hidden";
17039         }else if(this.hideMode == 'offsets'){
17040             ae.addClass('x-hidden');
17041         }else{
17042             ae.dom.style.display = "none";
17043         }
17044     },
17045
17046     /**
17047      * Convenience function to hide or show this component by boolean.
17048      * @param {Boolean} visible True to show, false to hide
17049      * @return {Roo.Component} this
17050      */
17051     setVisible: function(visible){
17052         if(visible) {
17053             this.show();
17054         }else{
17055             this.hide();
17056         }
17057         return this;
17058     },
17059
17060     /**
17061      * Returns true if this component is visible.
17062      */
17063     isVisible : function(){
17064         return this.getActionEl().isVisible();
17065     },
17066
17067     cloneConfig : function(overrides){
17068         overrides = overrides || {};
17069         var id = overrides.id || Roo.id();
17070         var cfg = Roo.applyIf(overrides, this.initialConfig);
17071         cfg.id = id; // prevent dup id
17072         return new this.constructor(cfg);
17073     }
17074 });/*
17075  * Based on:
17076  * Ext JS Library 1.1.1
17077  * Copyright(c) 2006-2007, Ext JS, LLC.
17078  *
17079  * Originally Released Under LGPL - original licence link has changed is not relivant.
17080  *
17081  * Fork - LGPL
17082  * <script type="text/javascript">
17083  */
17084
17085 /**
17086  * @class Roo.BoxComponent
17087  * @extends Roo.Component
17088  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17089  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17090  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17091  * layout containers.
17092  * @constructor
17093  * @param {Roo.Element/String/Object} config The configuration options.
17094  */
17095 Roo.BoxComponent = function(config){
17096     Roo.Component.call(this, config);
17097     this.addEvents({
17098         /**
17099          * @event resize
17100          * Fires after the component is resized.
17101              * @param {Roo.Component} this
17102              * @param {Number} adjWidth The box-adjusted width that was set
17103              * @param {Number} adjHeight The box-adjusted height that was set
17104              * @param {Number} rawWidth The width that was originally specified
17105              * @param {Number} rawHeight The height that was originally specified
17106              */
17107         resize : true,
17108         /**
17109          * @event move
17110          * Fires after the component is moved.
17111              * @param {Roo.Component} this
17112              * @param {Number} x The new x position
17113              * @param {Number} y The new y position
17114              */
17115         move : true
17116     });
17117 };
17118
17119 Roo.extend(Roo.BoxComponent, Roo.Component, {
17120     // private, set in afterRender to signify that the component has been rendered
17121     boxReady : false,
17122     // private, used to defer height settings to subclasses
17123     deferHeight: false,
17124     /** @cfg {Number} width
17125      * width (optional) size of component
17126      */
17127      /** @cfg {Number} height
17128      * height (optional) size of component
17129      */
17130      
17131     /**
17132      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17133      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17134      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17135      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17136      * @return {Roo.BoxComponent} this
17137      */
17138     setSize : function(w, h){
17139         // support for standard size objects
17140         if(typeof w == 'object'){
17141             h = w.height;
17142             w = w.width;
17143         }
17144         // not rendered
17145         if(!this.boxReady){
17146             this.width = w;
17147             this.height = h;
17148             return this;
17149         }
17150
17151         // prevent recalcs when not needed
17152         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17153             return this;
17154         }
17155         this.lastSize = {width: w, height: h};
17156
17157         var adj = this.adjustSize(w, h);
17158         var aw = adj.width, ah = adj.height;
17159         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17160             var rz = this.getResizeEl();
17161             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17162                 rz.setSize(aw, ah);
17163             }else if(!this.deferHeight && ah !== undefined){
17164                 rz.setHeight(ah);
17165             }else if(aw !== undefined){
17166                 rz.setWidth(aw);
17167             }
17168             this.onResize(aw, ah, w, h);
17169             this.fireEvent('resize', this, aw, ah, w, h);
17170         }
17171         return this;
17172     },
17173
17174     /**
17175      * Gets the current size of the component's underlying element.
17176      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17177      */
17178     getSize : function(){
17179         return this.el.getSize();
17180     },
17181
17182     /**
17183      * Gets the current XY position of the component's underlying element.
17184      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17185      * @return {Array} The XY position of the element (e.g., [100, 200])
17186      */
17187     getPosition : function(local){
17188         if(local === true){
17189             return [this.el.getLeft(true), this.el.getTop(true)];
17190         }
17191         return this.xy || this.el.getXY();
17192     },
17193
17194     /**
17195      * Gets the current box measurements of the component's underlying element.
17196      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17197      * @returns {Object} box An object in the format {x, y, width, height}
17198      */
17199     getBox : function(local){
17200         var s = this.el.getSize();
17201         if(local){
17202             s.x = this.el.getLeft(true);
17203             s.y = this.el.getTop(true);
17204         }else{
17205             var xy = this.xy || this.el.getXY();
17206             s.x = xy[0];
17207             s.y = xy[1];
17208         }
17209         return s;
17210     },
17211
17212     /**
17213      * Sets the current box measurements of the component's underlying element.
17214      * @param {Object} box An object in the format {x, y, width, height}
17215      * @returns {Roo.BoxComponent} this
17216      */
17217     updateBox : function(box){
17218         this.setSize(box.width, box.height);
17219         this.setPagePosition(box.x, box.y);
17220         return this;
17221     },
17222
17223     // protected
17224     getResizeEl : function(){
17225         return this.resizeEl || this.el;
17226     },
17227
17228     // protected
17229     getPositionEl : function(){
17230         return this.positionEl || this.el;
17231     },
17232
17233     /**
17234      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17235      * This method fires the move event.
17236      * @param {Number} left The new left
17237      * @param {Number} top The new top
17238      * @returns {Roo.BoxComponent} this
17239      */
17240     setPosition : function(x, y){
17241         this.x = x;
17242         this.y = y;
17243         if(!this.boxReady){
17244             return this;
17245         }
17246         var adj = this.adjustPosition(x, y);
17247         var ax = adj.x, ay = adj.y;
17248
17249         var el = this.getPositionEl();
17250         if(ax !== undefined || ay !== undefined){
17251             if(ax !== undefined && ay !== undefined){
17252                 el.setLeftTop(ax, ay);
17253             }else if(ax !== undefined){
17254                 el.setLeft(ax);
17255             }else if(ay !== undefined){
17256                 el.setTop(ay);
17257             }
17258             this.onPosition(ax, ay);
17259             this.fireEvent('move', this, ax, ay);
17260         }
17261         return this;
17262     },
17263
17264     /**
17265      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17266      * This method fires the move event.
17267      * @param {Number} x The new x position
17268      * @param {Number} y The new y position
17269      * @returns {Roo.BoxComponent} this
17270      */
17271     setPagePosition : function(x, y){
17272         this.pageX = x;
17273         this.pageY = y;
17274         if(!this.boxReady){
17275             return;
17276         }
17277         if(x === undefined || y === undefined){ // cannot translate undefined points
17278             return;
17279         }
17280         var p = this.el.translatePoints(x, y);
17281         this.setPosition(p.left, p.top);
17282         return this;
17283     },
17284
17285     // private
17286     onRender : function(ct, position){
17287         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17288         if(this.resizeEl){
17289             this.resizeEl = Roo.get(this.resizeEl);
17290         }
17291         if(this.positionEl){
17292             this.positionEl = Roo.get(this.positionEl);
17293         }
17294     },
17295
17296     // private
17297     afterRender : function(){
17298         Roo.BoxComponent.superclass.afterRender.call(this);
17299         this.boxReady = true;
17300         this.setSize(this.width, this.height);
17301         if(this.x || this.y){
17302             this.setPosition(this.x, this.y);
17303         }
17304         if(this.pageX || this.pageY){
17305             this.setPagePosition(this.pageX, this.pageY);
17306         }
17307     },
17308
17309     /**
17310      * Force the component's size to recalculate based on the underlying element's current height and width.
17311      * @returns {Roo.BoxComponent} this
17312      */
17313     syncSize : function(){
17314         delete this.lastSize;
17315         this.setSize(this.el.getWidth(), this.el.getHeight());
17316         return this;
17317     },
17318
17319     /**
17320      * Called after the component is resized, this method is empty by default but can be implemented by any
17321      * subclass that needs to perform custom logic after a resize occurs.
17322      * @param {Number} adjWidth The box-adjusted width that was set
17323      * @param {Number} adjHeight The box-adjusted height that was set
17324      * @param {Number} rawWidth The width that was originally specified
17325      * @param {Number} rawHeight The height that was originally specified
17326      */
17327     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17328
17329     },
17330
17331     /**
17332      * Called after the component is moved, this method is empty by default but can be implemented by any
17333      * subclass that needs to perform custom logic after a move occurs.
17334      * @param {Number} x The new x position
17335      * @param {Number} y The new y position
17336      */
17337     onPosition : function(x, y){
17338
17339     },
17340
17341     // private
17342     adjustSize : function(w, h){
17343         if(this.autoWidth){
17344             w = 'auto';
17345         }
17346         if(this.autoHeight){
17347             h = 'auto';
17348         }
17349         return {width : w, height: h};
17350     },
17351
17352     // private
17353     adjustPosition : function(x, y){
17354         return {x : x, y: y};
17355     }
17356 });/*
17357  * Based on:
17358  * Ext JS Library 1.1.1
17359  * Copyright(c) 2006-2007, Ext JS, LLC.
17360  *
17361  * Originally Released Under LGPL - original licence link has changed is not relivant.
17362  *
17363  * Fork - LGPL
17364  * <script type="text/javascript">
17365  */
17366  (function(){ 
17367 /**
17368  * @class Roo.Layer
17369  * @extends Roo.Element
17370  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17371  * automatic maintaining of shadow/shim positions.
17372  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17373  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17374  * you can pass a string with a CSS class name. False turns off the shadow.
17375  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17376  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17377  * @cfg {String} cls CSS class to add to the element
17378  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17379  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17380  * @constructor
17381  * @param {Object} config An object with config options.
17382  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17383  */
17384
17385 Roo.Layer = function(config, existingEl){
17386     config = config || {};
17387     var dh = Roo.DomHelper;
17388     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17389     if(existingEl){
17390         this.dom = Roo.getDom(existingEl);
17391     }
17392     if(!this.dom){
17393         var o = config.dh || {tag: "div", cls: "x-layer"};
17394         this.dom = dh.append(pel, o);
17395     }
17396     if(config.cls){
17397         this.addClass(config.cls);
17398     }
17399     this.constrain = config.constrain !== false;
17400     this.visibilityMode = Roo.Element.VISIBILITY;
17401     if(config.id){
17402         this.id = this.dom.id = config.id;
17403     }else{
17404         this.id = Roo.id(this.dom);
17405     }
17406     this.zindex = config.zindex || this.getZIndex();
17407     this.position("absolute", this.zindex);
17408     if(config.shadow){
17409         this.shadowOffset = config.shadowOffset || 4;
17410         this.shadow = new Roo.Shadow({
17411             offset : this.shadowOffset,
17412             mode : config.shadow
17413         });
17414     }else{
17415         this.shadowOffset = 0;
17416     }
17417     this.useShim = config.shim !== false && Roo.useShims;
17418     this.useDisplay = config.useDisplay;
17419     this.hide();
17420 };
17421
17422 var supr = Roo.Element.prototype;
17423
17424 // shims are shared among layer to keep from having 100 iframes
17425 var shims = [];
17426
17427 Roo.extend(Roo.Layer, Roo.Element, {
17428
17429     getZIndex : function(){
17430         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17431     },
17432
17433     getShim : function(){
17434         if(!this.useShim){
17435             return null;
17436         }
17437         if(this.shim){
17438             return this.shim;
17439         }
17440         var shim = shims.shift();
17441         if(!shim){
17442             shim = this.createShim();
17443             shim.enableDisplayMode('block');
17444             shim.dom.style.display = 'none';
17445             shim.dom.style.visibility = 'visible';
17446         }
17447         var pn = this.dom.parentNode;
17448         if(shim.dom.parentNode != pn){
17449             pn.insertBefore(shim.dom, this.dom);
17450         }
17451         shim.setStyle('z-index', this.getZIndex()-2);
17452         this.shim = shim;
17453         return shim;
17454     },
17455
17456     hideShim : function(){
17457         if(this.shim){
17458             this.shim.setDisplayed(false);
17459             shims.push(this.shim);
17460             delete this.shim;
17461         }
17462     },
17463
17464     disableShadow : function(){
17465         if(this.shadow){
17466             this.shadowDisabled = true;
17467             this.shadow.hide();
17468             this.lastShadowOffset = this.shadowOffset;
17469             this.shadowOffset = 0;
17470         }
17471     },
17472
17473     enableShadow : function(show){
17474         if(this.shadow){
17475             this.shadowDisabled = false;
17476             this.shadowOffset = this.lastShadowOffset;
17477             delete this.lastShadowOffset;
17478             if(show){
17479                 this.sync(true);
17480             }
17481         }
17482     },
17483
17484     // private
17485     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17486     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17487     sync : function(doShow){
17488         var sw = this.shadow;
17489         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17490             var sh = this.getShim();
17491
17492             var w = this.getWidth(),
17493                 h = this.getHeight();
17494
17495             var l = this.getLeft(true),
17496                 t = this.getTop(true);
17497
17498             if(sw && !this.shadowDisabled){
17499                 if(doShow && !sw.isVisible()){
17500                     sw.show(this);
17501                 }else{
17502                     sw.realign(l, t, w, h);
17503                 }
17504                 if(sh){
17505                     if(doShow){
17506                        sh.show();
17507                     }
17508                     // fit the shim behind the shadow, so it is shimmed too
17509                     var a = sw.adjusts, s = sh.dom.style;
17510                     s.left = (Math.min(l, l+a.l))+"px";
17511                     s.top = (Math.min(t, t+a.t))+"px";
17512                     s.width = (w+a.w)+"px";
17513                     s.height = (h+a.h)+"px";
17514                 }
17515             }else if(sh){
17516                 if(doShow){
17517                    sh.show();
17518                 }
17519                 sh.setSize(w, h);
17520                 sh.setLeftTop(l, t);
17521             }
17522             
17523         }
17524     },
17525
17526     // private
17527     destroy : function(){
17528         this.hideShim();
17529         if(this.shadow){
17530             this.shadow.hide();
17531         }
17532         this.removeAllListeners();
17533         var pn = this.dom.parentNode;
17534         if(pn){
17535             pn.removeChild(this.dom);
17536         }
17537         Roo.Element.uncache(this.id);
17538     },
17539
17540     remove : function(){
17541         this.destroy();
17542     },
17543
17544     // private
17545     beginUpdate : function(){
17546         this.updating = true;
17547     },
17548
17549     // private
17550     endUpdate : function(){
17551         this.updating = false;
17552         this.sync(true);
17553     },
17554
17555     // private
17556     hideUnders : function(negOffset){
17557         if(this.shadow){
17558             this.shadow.hide();
17559         }
17560         this.hideShim();
17561     },
17562
17563     // private
17564     constrainXY : function(){
17565         if(this.constrain){
17566             var vw = Roo.lib.Dom.getViewWidth(),
17567                 vh = Roo.lib.Dom.getViewHeight();
17568             var s = Roo.get(document).getScroll();
17569
17570             var xy = this.getXY();
17571             var x = xy[0], y = xy[1];   
17572             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17573             // only move it if it needs it
17574             var moved = false;
17575             // first validate right/bottom
17576             if((x + w) > vw+s.left){
17577                 x = vw - w - this.shadowOffset;
17578                 moved = true;
17579             }
17580             if((y + h) > vh+s.top){
17581                 y = vh - h - this.shadowOffset;
17582                 moved = true;
17583             }
17584             // then make sure top/left isn't negative
17585             if(x < s.left){
17586                 x = s.left;
17587                 moved = true;
17588             }
17589             if(y < s.top){
17590                 y = s.top;
17591                 moved = true;
17592             }
17593             if(moved){
17594                 if(this.avoidY){
17595                     var ay = this.avoidY;
17596                     if(y <= ay && (y+h) >= ay){
17597                         y = ay-h-5;   
17598                     }
17599                 }
17600                 xy = [x, y];
17601                 this.storeXY(xy);
17602                 supr.setXY.call(this, xy);
17603                 this.sync();
17604             }
17605         }
17606     },
17607
17608     isVisible : function(){
17609         return this.visible;    
17610     },
17611
17612     // private
17613     showAction : function(){
17614         this.visible = true; // track visibility to prevent getStyle calls
17615         if(this.useDisplay === true){
17616             this.setDisplayed("");
17617         }else if(this.lastXY){
17618             supr.setXY.call(this, this.lastXY);
17619         }else if(this.lastLT){
17620             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17621         }
17622     },
17623
17624     // private
17625     hideAction : function(){
17626         this.visible = false;
17627         if(this.useDisplay === true){
17628             this.setDisplayed(false);
17629         }else{
17630             this.setLeftTop(-10000,-10000);
17631         }
17632     },
17633
17634     // overridden Element method
17635     setVisible : function(v, a, d, c, e){
17636         if(v){
17637             this.showAction();
17638         }
17639         if(a && v){
17640             var cb = function(){
17641                 this.sync(true);
17642                 if(c){
17643                     c();
17644                 }
17645             }.createDelegate(this);
17646             supr.setVisible.call(this, true, true, d, cb, e);
17647         }else{
17648             if(!v){
17649                 this.hideUnders(true);
17650             }
17651             var cb = c;
17652             if(a){
17653                 cb = function(){
17654                     this.hideAction();
17655                     if(c){
17656                         c();
17657                     }
17658                 }.createDelegate(this);
17659             }
17660             supr.setVisible.call(this, v, a, d, cb, e);
17661             if(v){
17662                 this.sync(true);
17663             }else if(!a){
17664                 this.hideAction();
17665             }
17666         }
17667     },
17668
17669     storeXY : function(xy){
17670         delete this.lastLT;
17671         this.lastXY = xy;
17672     },
17673
17674     storeLeftTop : function(left, top){
17675         delete this.lastXY;
17676         this.lastLT = [left, top];
17677     },
17678
17679     // private
17680     beforeFx : function(){
17681         this.beforeAction();
17682         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17683     },
17684
17685     // private
17686     afterFx : function(){
17687         Roo.Layer.superclass.afterFx.apply(this, arguments);
17688         this.sync(this.isVisible());
17689     },
17690
17691     // private
17692     beforeAction : function(){
17693         if(!this.updating && this.shadow){
17694             this.shadow.hide();
17695         }
17696     },
17697
17698     // overridden Element method
17699     setLeft : function(left){
17700         this.storeLeftTop(left, this.getTop(true));
17701         supr.setLeft.apply(this, arguments);
17702         this.sync();
17703     },
17704
17705     setTop : function(top){
17706         this.storeLeftTop(this.getLeft(true), top);
17707         supr.setTop.apply(this, arguments);
17708         this.sync();
17709     },
17710
17711     setLeftTop : function(left, top){
17712         this.storeLeftTop(left, top);
17713         supr.setLeftTop.apply(this, arguments);
17714         this.sync();
17715     },
17716
17717     setXY : function(xy, a, d, c, e){
17718         this.fixDisplay();
17719         this.beforeAction();
17720         this.storeXY(xy);
17721         var cb = this.createCB(c);
17722         supr.setXY.call(this, xy, a, d, cb, e);
17723         if(!a){
17724             cb();
17725         }
17726     },
17727
17728     // private
17729     createCB : function(c){
17730         var el = this;
17731         return function(){
17732             el.constrainXY();
17733             el.sync(true);
17734             if(c){
17735                 c();
17736             }
17737         };
17738     },
17739
17740     // overridden Element method
17741     setX : function(x, a, d, c, e){
17742         this.setXY([x, this.getY()], a, d, c, e);
17743     },
17744
17745     // overridden Element method
17746     setY : function(y, a, d, c, e){
17747         this.setXY([this.getX(), y], a, d, c, e);
17748     },
17749
17750     // overridden Element method
17751     setSize : function(w, h, a, d, c, e){
17752         this.beforeAction();
17753         var cb = this.createCB(c);
17754         supr.setSize.call(this, w, h, a, d, cb, e);
17755         if(!a){
17756             cb();
17757         }
17758     },
17759
17760     // overridden Element method
17761     setWidth : function(w, a, d, c, e){
17762         this.beforeAction();
17763         var cb = this.createCB(c);
17764         supr.setWidth.call(this, w, a, d, cb, e);
17765         if(!a){
17766             cb();
17767         }
17768     },
17769
17770     // overridden Element method
17771     setHeight : function(h, a, d, c, e){
17772         this.beforeAction();
17773         var cb = this.createCB(c);
17774         supr.setHeight.call(this, h, a, d, cb, e);
17775         if(!a){
17776             cb();
17777         }
17778     },
17779
17780     // overridden Element method
17781     setBounds : function(x, y, w, h, a, d, c, e){
17782         this.beforeAction();
17783         var cb = this.createCB(c);
17784         if(!a){
17785             this.storeXY([x, y]);
17786             supr.setXY.call(this, [x, y]);
17787             supr.setSize.call(this, w, h, a, d, cb, e);
17788             cb();
17789         }else{
17790             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17791         }
17792         return this;
17793     },
17794     
17795     /**
17796      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17797      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17798      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17799      * @param {Number} zindex The new z-index to set
17800      * @return {this} The Layer
17801      */
17802     setZIndex : function(zindex){
17803         this.zindex = zindex;
17804         this.setStyle("z-index", zindex + 2);
17805         if(this.shadow){
17806             this.shadow.setZIndex(zindex + 1);
17807         }
17808         if(this.shim){
17809             this.shim.setStyle("z-index", zindex);
17810         }
17811     }
17812 });
17813 })();/*
17814  * Original code for Roojs - LGPL
17815  * <script type="text/javascript">
17816  */
17817  
17818 /**
17819  * @class Roo.XComponent
17820  * A delayed Element creator...
17821  * Or a way to group chunks of interface together.
17822  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17823  *  used in conjunction with XComponent.build() it will create an instance of each element,
17824  *  then call addxtype() to build the User interface.
17825  * 
17826  * Mypart.xyx = new Roo.XComponent({
17827
17828     parent : 'Mypart.xyz', // empty == document.element.!!
17829     order : '001',
17830     name : 'xxxx'
17831     region : 'xxxx'
17832     disabled : function() {} 
17833      
17834     tree : function() { // return an tree of xtype declared components
17835         var MODULE = this;
17836         return 
17837         {
17838             xtype : 'NestedLayoutPanel',
17839             // technicall
17840         }
17841      ]
17842  *})
17843  *
17844  *
17845  * It can be used to build a big heiracy, with parent etc.
17846  * or you can just use this to render a single compoent to a dom element
17847  * MYPART.render(Roo.Element | String(id) | dom_element )
17848  *
17849  *
17850  * Usage patterns.
17851  *
17852  * Classic Roo
17853  *
17854  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17855  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17856  *
17857  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17858  *
17859  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17860  * - if mulitple topModules exist, the last one is defined as the top module.
17861  *
17862  * Embeded Roo
17863  * 
17864  * When the top level or multiple modules are to embedded into a existing HTML page,
17865  * the parent element can container '#id' of the element where the module will be drawn.
17866  *
17867  * Bootstrap Roo
17868  *
17869  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17870  * it relies more on a include mechanism, where sub modules are included into an outer page.
17871  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17872  * 
17873  * Bootstrap Roo Included elements
17874  *
17875  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17876  * hence confusing the component builder as it thinks there are multiple top level elements. 
17877  *
17878  * String Over-ride & Translations
17879  *
17880  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17881  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17882  * are needed. @see Roo.XComponent.overlayString  
17883  * 
17884  * 
17885  * 
17886  * @extends Roo.util.Observable
17887  * @constructor
17888  * @param cfg {Object} configuration of component
17889  * 
17890  */
17891 Roo.XComponent = function(cfg) {
17892     Roo.apply(this, cfg);
17893     this.addEvents({ 
17894         /**
17895              * @event built
17896              * Fires when this the componnt is built
17897              * @param {Roo.XComponent} c the component
17898              */
17899         'built' : true
17900         
17901     });
17902     this.region = this.region || 'center'; // default..
17903     Roo.XComponent.register(this);
17904     this.modules = false;
17905     this.el = false; // where the layout goes..
17906     
17907     
17908 }
17909 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17910     /**
17911      * @property el
17912      * The created element (with Roo.factory())
17913      * @type {Roo.Layout}
17914      */
17915     el  : false,
17916     
17917     /**
17918      * @property el
17919      * for BC  - use el in new code
17920      * @type {Roo.Layout}
17921      */
17922     panel : false,
17923     
17924     /**
17925      * @property layout
17926      * for BC  - use el in new code
17927      * @type {Roo.Layout}
17928      */
17929     layout : false,
17930     
17931      /**
17932      * @cfg {Function|boolean} disabled
17933      * If this module is disabled by some rule, return true from the funtion
17934      */
17935     disabled : false,
17936     
17937     /**
17938      * @cfg {String} parent 
17939      * Name of parent element which it get xtype added to..
17940      */
17941     parent: false,
17942     
17943     /**
17944      * @cfg {String} order
17945      * Used to set the order in which elements are created (usefull for multiple tabs)
17946      */
17947     
17948     order : false,
17949     /**
17950      * @cfg {String} name
17951      * String to display while loading.
17952      */
17953     name : false,
17954     /**
17955      * @cfg {String} region
17956      * Region to render component to (defaults to center)
17957      */
17958     region : 'center',
17959     
17960     /**
17961      * @cfg {Array} items
17962      * A single item array - the first element is the root of the tree..
17963      * It's done this way to stay compatible with the Xtype system...
17964      */
17965     items : false,
17966     
17967     /**
17968      * @property _tree
17969      * The method that retuns the tree of parts that make up this compoennt 
17970      * @type {function}
17971      */
17972     _tree  : false,
17973     
17974      /**
17975      * render
17976      * render element to dom or tree
17977      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17978      */
17979     
17980     render : function(el)
17981     {
17982         
17983         el = el || false;
17984         var hp = this.parent ? 1 : 0;
17985         Roo.debug &&  Roo.log(this);
17986         
17987         var tree = this._tree ? this._tree() : this.tree();
17988
17989         
17990         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17991             // if parent is a '#.....' string, then let's use that..
17992             var ename = this.parent.substr(1);
17993             this.parent = false;
17994             Roo.debug && Roo.log(ename);
17995             switch (ename) {
17996                 case 'bootstrap-body':
17997                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17998                         // this is the BorderLayout standard?
17999                        this.parent = { el : true };
18000                        break;
18001                     }
18002                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18003                         // need to insert stuff...
18004                         this.parent =  {
18005                              el : new Roo.bootstrap.layout.Border({
18006                                  el : document.body, 
18007                      
18008                                  center: {
18009                                     titlebar: false,
18010                                     autoScroll:false,
18011                                     closeOnTab: true,
18012                                     tabPosition: 'top',
18013                                       //resizeTabs: true,
18014                                     alwaysShowTabs: true,
18015                                     hideTabs: false
18016                                      //minTabWidth: 140
18017                                  }
18018                              })
18019                         
18020                          };
18021                          break;
18022                     }
18023                          
18024                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18025                         this.parent = { el :  new  Roo.bootstrap.Body() };
18026                         Roo.debug && Roo.log("setting el to doc body");
18027                          
18028                     } else {
18029                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18030                     }
18031                     break;
18032                 case 'bootstrap':
18033                     this.parent = { el : true};
18034                     // fall through
18035                 default:
18036                     el = Roo.get(ename);
18037                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18038                         this.parent = { el : true};
18039                     }
18040                     
18041                     break;
18042             }
18043                 
18044             
18045             if (!el && !this.parent) {
18046                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18047                 return;
18048             }
18049         }
18050         
18051         Roo.debug && Roo.log("EL:");
18052         Roo.debug && Roo.log(el);
18053         Roo.debug && Roo.log("this.parent.el:");
18054         Roo.debug && Roo.log(this.parent.el);
18055         
18056
18057         // altertive root elements ??? - we need a better way to indicate these.
18058         var is_alt = Roo.XComponent.is_alt ||
18059                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18060                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18061                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18062         
18063         
18064         
18065         if (!this.parent && is_alt) {
18066             //el = Roo.get(document.body);
18067             this.parent = { el : true };
18068         }
18069             
18070             
18071         
18072         if (!this.parent) {
18073             
18074             Roo.debug && Roo.log("no parent - creating one");
18075             
18076             el = el ? Roo.get(el) : false;      
18077             
18078             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18079                 
18080                 this.parent =  {
18081                     el : new Roo.bootstrap.layout.Border({
18082                         el: el || document.body,
18083                     
18084                         center: {
18085                             titlebar: false,
18086                             autoScroll:false,
18087                             closeOnTab: true,
18088                             tabPosition: 'top',
18089                              //resizeTabs: true,
18090                             alwaysShowTabs: false,
18091                             hideTabs: true,
18092                             minTabWidth: 140,
18093                             overflow: 'visible'
18094                          }
18095                      })
18096                 };
18097             } else {
18098             
18099                 // it's a top level one..
18100                 this.parent =  {
18101                     el : new Roo.BorderLayout(el || document.body, {
18102                         center: {
18103                             titlebar: false,
18104                             autoScroll:false,
18105                             closeOnTab: true,
18106                             tabPosition: 'top',
18107                              //resizeTabs: true,
18108                             alwaysShowTabs: el && hp? false :  true,
18109                             hideTabs: el || !hp ? true :  false,
18110                             minTabWidth: 140
18111                          }
18112                     })
18113                 };
18114             }
18115         }
18116         
18117         if (!this.parent.el) {
18118                 // probably an old style ctor, which has been disabled.
18119                 return;
18120
18121         }
18122                 // The 'tree' method is  '_tree now' 
18123             
18124         tree.region = tree.region || this.region;
18125         var is_body = false;
18126         if (this.parent.el === true) {
18127             // bootstrap... - body..
18128             if (el) {
18129                 tree.el = el;
18130             }
18131             this.parent.el = Roo.factory(tree);
18132             is_body = true;
18133         }
18134         
18135         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18136         this.fireEvent('built', this);
18137         
18138         this.panel = this.el;
18139         this.layout = this.panel.layout;
18140         this.parentLayout = this.parent.layout  || false;  
18141          
18142     }
18143     
18144 });
18145
18146 Roo.apply(Roo.XComponent, {
18147     /**
18148      * @property  hideProgress
18149      * true to disable the building progress bar.. usefull on single page renders.
18150      * @type Boolean
18151      */
18152     hideProgress : false,
18153     /**
18154      * @property  buildCompleted
18155      * True when the builder has completed building the interface.
18156      * @type Boolean
18157      */
18158     buildCompleted : false,
18159      
18160     /**
18161      * @property  topModule
18162      * the upper most module - uses document.element as it's constructor.
18163      * @type Object
18164      */
18165      
18166     topModule  : false,
18167       
18168     /**
18169      * @property  modules
18170      * array of modules to be created by registration system.
18171      * @type {Array} of Roo.XComponent
18172      */
18173     
18174     modules : [],
18175     /**
18176      * @property  elmodules
18177      * array of modules to be created by which use #ID 
18178      * @type {Array} of Roo.XComponent
18179      */
18180      
18181     elmodules : [],
18182
18183      /**
18184      * @property  is_alt
18185      * Is an alternative Root - normally used by bootstrap or other systems,
18186      *    where the top element in the tree can wrap 'body' 
18187      * @type {boolean}  (default false)
18188      */
18189      
18190     is_alt : false,
18191     /**
18192      * @property  build_from_html
18193      * Build elements from html - used by bootstrap HTML stuff 
18194      *    - this is cleared after build is completed
18195      * @type {boolean}    (default false)
18196      */
18197      
18198     build_from_html : false,
18199     /**
18200      * Register components to be built later.
18201      *
18202      * This solves the following issues
18203      * - Building is not done on page load, but after an authentication process has occured.
18204      * - Interface elements are registered on page load
18205      * - Parent Interface elements may not be loaded before child, so this handles that..
18206      * 
18207      *
18208      * example:
18209      * 
18210      * MyApp.register({
18211           order : '000001',
18212           module : 'Pman.Tab.projectMgr',
18213           region : 'center',
18214           parent : 'Pman.layout',
18215           disabled : false,  // or use a function..
18216         })
18217      
18218      * * @param {Object} details about module
18219      */
18220     register : function(obj) {
18221                 
18222         Roo.XComponent.event.fireEvent('register', obj);
18223         switch(typeof(obj.disabled) ) {
18224                 
18225             case 'undefined':
18226                 break;
18227             
18228             case 'function':
18229                 if ( obj.disabled() ) {
18230                         return;
18231                 }
18232                 break;
18233             
18234             default:
18235                 if (obj.disabled || obj.region == '#disabled') {
18236                         return;
18237                 }
18238                 break;
18239         }
18240                 
18241         this.modules.push(obj);
18242          
18243     },
18244     /**
18245      * convert a string to an object..
18246      * eg. 'AAA.BBB' -> finds AAA.BBB
18247
18248      */
18249     
18250     toObject : function(str)
18251     {
18252         if (!str || typeof(str) == 'object') {
18253             return str;
18254         }
18255         if (str.substring(0,1) == '#') {
18256             return str;
18257         }
18258
18259         var ar = str.split('.');
18260         var rt, o;
18261         rt = ar.shift();
18262             /** eval:var:o */
18263         try {
18264             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18265         } catch (e) {
18266             throw "Module not found : " + str;
18267         }
18268         
18269         if (o === false) {
18270             throw "Module not found : " + str;
18271         }
18272         Roo.each(ar, function(e) {
18273             if (typeof(o[e]) == 'undefined') {
18274                 throw "Module not found : " + str;
18275             }
18276             o = o[e];
18277         });
18278         
18279         return o;
18280         
18281     },
18282     
18283     
18284     /**
18285      * move modules into their correct place in the tree..
18286      * 
18287      */
18288     preBuild : function ()
18289     {
18290         var _t = this;
18291         Roo.each(this.modules , function (obj)
18292         {
18293             Roo.XComponent.event.fireEvent('beforebuild', obj);
18294             
18295             var opar = obj.parent;
18296             try { 
18297                 obj.parent = this.toObject(opar);
18298             } catch(e) {
18299                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18300                 return;
18301             }
18302             
18303             if (!obj.parent) {
18304                 Roo.debug && Roo.log("GOT top level module");
18305                 Roo.debug && Roo.log(obj);
18306                 obj.modules = new Roo.util.MixedCollection(false, 
18307                     function(o) { return o.order + '' }
18308                 );
18309                 this.topModule = obj;
18310                 return;
18311             }
18312                         // parent is a string (usually a dom element name..)
18313             if (typeof(obj.parent) == 'string') {
18314                 this.elmodules.push(obj);
18315                 return;
18316             }
18317             if (obj.parent.constructor != Roo.XComponent) {
18318                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18319             }
18320             if (!obj.parent.modules) {
18321                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18322                     function(o) { return o.order + '' }
18323                 );
18324             }
18325             if (obj.parent.disabled) {
18326                 obj.disabled = true;
18327             }
18328             obj.parent.modules.add(obj);
18329         }, this);
18330     },
18331     
18332      /**
18333      * make a list of modules to build.
18334      * @return {Array} list of modules. 
18335      */ 
18336     
18337     buildOrder : function()
18338     {
18339         var _this = this;
18340         var cmp = function(a,b) {   
18341             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18342         };
18343         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18344             throw "No top level modules to build";
18345         }
18346         
18347         // make a flat list in order of modules to build.
18348         var mods = this.topModule ? [ this.topModule ] : [];
18349                 
18350         
18351         // elmodules (is a list of DOM based modules )
18352         Roo.each(this.elmodules, function(e) {
18353             mods.push(e);
18354             if (!this.topModule &&
18355                 typeof(e.parent) == 'string' &&
18356                 e.parent.substring(0,1) == '#' &&
18357                 Roo.get(e.parent.substr(1))
18358                ) {
18359                 
18360                 _this.topModule = e;
18361             }
18362             
18363         });
18364
18365         
18366         // add modules to their parents..
18367         var addMod = function(m) {
18368             Roo.debug && Roo.log("build Order: add: " + m.name);
18369                 
18370             mods.push(m);
18371             if (m.modules && !m.disabled) {
18372                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18373                 m.modules.keySort('ASC',  cmp );
18374                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18375     
18376                 m.modules.each(addMod);
18377             } else {
18378                 Roo.debug && Roo.log("build Order: no child modules");
18379             }
18380             // not sure if this is used any more..
18381             if (m.finalize) {
18382                 m.finalize.name = m.name + " (clean up) ";
18383                 mods.push(m.finalize);
18384             }
18385             
18386         }
18387         if (this.topModule && this.topModule.modules) { 
18388             this.topModule.modules.keySort('ASC',  cmp );
18389             this.topModule.modules.each(addMod);
18390         } 
18391         return mods;
18392     },
18393     
18394      /**
18395      * Build the registered modules.
18396      * @param {Object} parent element.
18397      * @param {Function} optional method to call after module has been added.
18398      * 
18399      */ 
18400    
18401     build : function(opts) 
18402     {
18403         
18404         if (typeof(opts) != 'undefined') {
18405             Roo.apply(this,opts);
18406         }
18407         
18408         this.preBuild();
18409         var mods = this.buildOrder();
18410       
18411         //this.allmods = mods;
18412         //Roo.debug && Roo.log(mods);
18413         //return;
18414         if (!mods.length) { // should not happen
18415             throw "NO modules!!!";
18416         }
18417         
18418         
18419         var msg = "Building Interface...";
18420         // flash it up as modal - so we store the mask!?
18421         if (!this.hideProgress && Roo.MessageBox) {
18422             Roo.MessageBox.show({ title: 'loading' });
18423             Roo.MessageBox.show({
18424                title: "Please wait...",
18425                msg: msg,
18426                width:450,
18427                progress:true,
18428                buttons : false,
18429                closable:false,
18430                modal: false
18431               
18432             });
18433         }
18434         var total = mods.length;
18435         
18436         var _this = this;
18437         var progressRun = function() {
18438             if (!mods.length) {
18439                 Roo.debug && Roo.log('hide?');
18440                 if (!this.hideProgress && Roo.MessageBox) {
18441                     Roo.MessageBox.hide();
18442                 }
18443                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18444                 
18445                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18446                 
18447                 // THE END...
18448                 return false;   
18449             }
18450             
18451             var m = mods.shift();
18452             
18453             
18454             Roo.debug && Roo.log(m);
18455             // not sure if this is supported any more.. - modules that are are just function
18456             if (typeof(m) == 'function') { 
18457                 m.call(this);
18458                 return progressRun.defer(10, _this);
18459             } 
18460             
18461             
18462             msg = "Building Interface " + (total  - mods.length) + 
18463                     " of " + total + 
18464                     (m.name ? (' - ' + m.name) : '');
18465                         Roo.debug && Roo.log(msg);
18466             if (!_this.hideProgress &&  Roo.MessageBox) { 
18467                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18468             }
18469             
18470          
18471             // is the module disabled?
18472             var disabled = (typeof(m.disabled) == 'function') ?
18473                 m.disabled.call(m.module.disabled) : m.disabled;    
18474             
18475             
18476             if (disabled) {
18477                 return progressRun(); // we do not update the display!
18478             }
18479             
18480             // now build 
18481             
18482                         
18483                         
18484             m.render();
18485             // it's 10 on top level, and 1 on others??? why...
18486             return progressRun.defer(10, _this);
18487              
18488         }
18489         progressRun.defer(1, _this);
18490      
18491         
18492         
18493     },
18494     /**
18495      * Overlay a set of modified strings onto a component
18496      * This is dependant on our builder exporting the strings and 'named strings' elements.
18497      * 
18498      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18499      * @param {Object} associative array of 'named' string and it's new value.
18500      * 
18501      */
18502         overlayStrings : function( component, strings )
18503     {
18504         if (typeof(component['_named_strings']) == 'undefined') {
18505             throw "ERROR: component does not have _named_strings";
18506         }
18507         for ( var k in strings ) {
18508             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18509             if (md !== false) {
18510                 component['_strings'][md] = strings[k];
18511             } else {
18512                 Roo.log('could not find named string: ' + k + ' in');
18513                 Roo.log(component);
18514             }
18515             
18516         }
18517         
18518     },
18519     
18520         
18521         /**
18522          * Event Object.
18523          *
18524          *
18525          */
18526         event: false, 
18527     /**
18528          * wrapper for event.on - aliased later..  
18529          * Typically use to register a event handler for register:
18530          *
18531          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18532          *
18533          */
18534     on : false
18535    
18536     
18537     
18538 });
18539
18540 Roo.XComponent.event = new Roo.util.Observable({
18541                 events : { 
18542                         /**
18543                          * @event register
18544                          * Fires when an Component is registered,
18545                          * set the disable property on the Component to stop registration.
18546                          * @param {Roo.XComponent} c the component being registerd.
18547                          * 
18548                          */
18549                         'register' : true,
18550             /**
18551                          * @event beforebuild
18552                          * Fires before each Component is built
18553                          * can be used to apply permissions.
18554                          * @param {Roo.XComponent} c the component being registerd.
18555                          * 
18556                          */
18557                         'beforebuild' : true,
18558                         /**
18559                          * @event buildcomplete
18560                          * Fires on the top level element when all elements have been built
18561                          * @param {Roo.XComponent} the top level component.
18562                          */
18563                         'buildcomplete' : true
18564                         
18565                 }
18566 });
18567
18568 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18569  //
18570  /**
18571  * marked - a markdown parser
18572  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18573  * https://github.com/chjj/marked
18574  */
18575
18576
18577 /**
18578  *
18579  * Roo.Markdown - is a very crude wrapper around marked..
18580  *
18581  * usage:
18582  * 
18583  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18584  * 
18585  * Note: move the sample code to the bottom of this
18586  * file before uncommenting it.
18587  *
18588  */
18589
18590 Roo.Markdown = {};
18591 Roo.Markdown.toHtml = function(text) {
18592     
18593     var c = new Roo.Markdown.marked.setOptions({
18594             renderer: new Roo.Markdown.marked.Renderer(),
18595             gfm: true,
18596             tables: true,
18597             breaks: false,
18598             pedantic: false,
18599             sanitize: false,
18600             smartLists: true,
18601             smartypants: false
18602           });
18603     // A FEW HACKS!!?
18604     
18605     text = text.replace(/\\\n/g,' ');
18606     return Roo.Markdown.marked(text);
18607 };
18608 //
18609 // converter
18610 //
18611 // Wraps all "globals" so that the only thing
18612 // exposed is makeHtml().
18613 //
18614 (function() {
18615     
18616      /**
18617          * eval:var:escape
18618          * eval:var:unescape
18619          * eval:var:replace
18620          */
18621       
18622     /**
18623      * Helpers
18624      */
18625     
18626     var escape = function (html, encode) {
18627       return html
18628         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18629         .replace(/</g, '&lt;')
18630         .replace(/>/g, '&gt;')
18631         .replace(/"/g, '&quot;')
18632         .replace(/'/g, '&#39;');
18633     }
18634     
18635     var unescape = function (html) {
18636         // explicitly match decimal, hex, and named HTML entities 
18637       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18638         n = n.toLowerCase();
18639         if (n === 'colon') { return ':'; }
18640         if (n.charAt(0) === '#') {
18641           return n.charAt(1) === 'x'
18642             ? String.fromCharCode(parseInt(n.substring(2), 16))
18643             : String.fromCharCode(+n.substring(1));
18644         }
18645         return '';
18646       });
18647     }
18648     
18649     var replace = function (regex, opt) {
18650       regex = regex.source;
18651       opt = opt || '';
18652       return function self(name, val) {
18653         if (!name) { return new RegExp(regex, opt); }
18654         val = val.source || val;
18655         val = val.replace(/(^|[^\[])\^/g, '$1');
18656         regex = regex.replace(name, val);
18657         return self;
18658       };
18659     }
18660
18661
18662          /**
18663          * eval:var:noop
18664     */
18665     var noop = function () {}
18666     noop.exec = noop;
18667     
18668          /**
18669          * eval:var:merge
18670     */
18671     var merge = function (obj) {
18672       var i = 1
18673         , target
18674         , key;
18675     
18676       for (; i < arguments.length; i++) {
18677         target = arguments[i];
18678         for (key in target) {
18679           if (Object.prototype.hasOwnProperty.call(target, key)) {
18680             obj[key] = target[key];
18681           }
18682         }
18683       }
18684     
18685       return obj;
18686     }
18687     
18688     
18689     /**
18690      * Block-Level Grammar
18691      */
18692     
18693     
18694     
18695     
18696     var block = {
18697       newline: /^\n+/,
18698       code: /^( {4}[^\n]+\n*)+/,
18699       fences: noop,
18700       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18701       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18702       nptable: noop,
18703       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18704       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18705       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18706       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18707       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18708       table: noop,
18709       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18710       text: /^[^\n]+/
18711     };
18712     
18713     block.bullet = /(?:[*+-]|\d+\.)/;
18714     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18715     block.item = replace(block.item, 'gm')
18716       (/bull/g, block.bullet)
18717       ();
18718     
18719     block.list = replace(block.list)
18720       (/bull/g, block.bullet)
18721       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18722       ('def', '\\n+(?=' + block.def.source + ')')
18723       ();
18724     
18725     block.blockquote = replace(block.blockquote)
18726       ('def', block.def)
18727       ();
18728     
18729     block._tag = '(?!(?:'
18730       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18731       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18732       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18733     
18734     block.html = replace(block.html)
18735       ('comment', /<!--[\s\S]*?-->/)
18736       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18737       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18738       (/tag/g, block._tag)
18739       ();
18740     
18741     block.paragraph = replace(block.paragraph)
18742       ('hr', block.hr)
18743       ('heading', block.heading)
18744       ('lheading', block.lheading)
18745       ('blockquote', block.blockquote)
18746       ('tag', '<' + block._tag)
18747       ('def', block.def)
18748       ();
18749     
18750     /**
18751      * Normal Block Grammar
18752      */
18753     
18754     block.normal = merge({}, block);
18755     
18756     /**
18757      * GFM Block Grammar
18758      */
18759     
18760     block.gfm = merge({}, block.normal, {
18761       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18762       paragraph: /^/,
18763       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18764     });
18765     
18766     block.gfm.paragraph = replace(block.paragraph)
18767       ('(?!', '(?!'
18768         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18769         + block.list.source.replace('\\1', '\\3') + '|')
18770       ();
18771     
18772     /**
18773      * GFM + Tables Block Grammar
18774      */
18775     
18776     block.tables = merge({}, block.gfm, {
18777       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18778       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18779     });
18780     
18781     /**
18782      * Block Lexer
18783      */
18784     
18785     var Lexer = function (options) {
18786       this.tokens = [];
18787       this.tokens.links = {};
18788       this.options = options || marked.defaults;
18789       this.rules = block.normal;
18790     
18791       if (this.options.gfm) {
18792         if (this.options.tables) {
18793           this.rules = block.tables;
18794         } else {
18795           this.rules = block.gfm;
18796         }
18797       }
18798     }
18799     
18800     /**
18801      * Expose Block Rules
18802      */
18803     
18804     Lexer.rules = block;
18805     
18806     /**
18807      * Static Lex Method
18808      */
18809     
18810     Lexer.lex = function(src, options) {
18811       var lexer = new Lexer(options);
18812       return lexer.lex(src);
18813     };
18814     
18815     /**
18816      * Preprocessing
18817      */
18818     
18819     Lexer.prototype.lex = function(src) {
18820       src = src
18821         .replace(/\r\n|\r/g, '\n')
18822         .replace(/\t/g, '    ')
18823         .replace(/\u00a0/g, ' ')
18824         .replace(/\u2424/g, '\n');
18825     
18826       return this.token(src, true);
18827     };
18828     
18829     /**
18830      * Lexing
18831      */
18832     
18833     Lexer.prototype.token = function(src, top, bq) {
18834       var src = src.replace(/^ +$/gm, '')
18835         , next
18836         , loose
18837         , cap
18838         , bull
18839         , b
18840         , item
18841         , space
18842         , i
18843         , l;
18844     
18845       while (src) {
18846         // newline
18847         if (cap = this.rules.newline.exec(src)) {
18848           src = src.substring(cap[0].length);
18849           if (cap[0].length > 1) {
18850             this.tokens.push({
18851               type: 'space'
18852             });
18853           }
18854         }
18855     
18856         // code
18857         if (cap = this.rules.code.exec(src)) {
18858           src = src.substring(cap[0].length);
18859           cap = cap[0].replace(/^ {4}/gm, '');
18860           this.tokens.push({
18861             type: 'code',
18862             text: !this.options.pedantic
18863               ? cap.replace(/\n+$/, '')
18864               : cap
18865           });
18866           continue;
18867         }
18868     
18869         // fences (gfm)
18870         if (cap = this.rules.fences.exec(src)) {
18871           src = src.substring(cap[0].length);
18872           this.tokens.push({
18873             type: 'code',
18874             lang: cap[2],
18875             text: cap[3] || ''
18876           });
18877           continue;
18878         }
18879     
18880         // heading
18881         if (cap = this.rules.heading.exec(src)) {
18882           src = src.substring(cap[0].length);
18883           this.tokens.push({
18884             type: 'heading',
18885             depth: cap[1].length,
18886             text: cap[2]
18887           });
18888           continue;
18889         }
18890     
18891         // table no leading pipe (gfm)
18892         if (top && (cap = this.rules.nptable.exec(src))) {
18893           src = src.substring(cap[0].length);
18894     
18895           item = {
18896             type: 'table',
18897             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18898             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18899             cells: cap[3].replace(/\n$/, '').split('\n')
18900           };
18901     
18902           for (i = 0; i < item.align.length; i++) {
18903             if (/^ *-+: *$/.test(item.align[i])) {
18904               item.align[i] = 'right';
18905             } else if (/^ *:-+: *$/.test(item.align[i])) {
18906               item.align[i] = 'center';
18907             } else if (/^ *:-+ *$/.test(item.align[i])) {
18908               item.align[i] = 'left';
18909             } else {
18910               item.align[i] = null;
18911             }
18912           }
18913     
18914           for (i = 0; i < item.cells.length; i++) {
18915             item.cells[i] = item.cells[i].split(/ *\| */);
18916           }
18917     
18918           this.tokens.push(item);
18919     
18920           continue;
18921         }
18922     
18923         // lheading
18924         if (cap = this.rules.lheading.exec(src)) {
18925           src = src.substring(cap[0].length);
18926           this.tokens.push({
18927             type: 'heading',
18928             depth: cap[2] === '=' ? 1 : 2,
18929             text: cap[1]
18930           });
18931           continue;
18932         }
18933     
18934         // hr
18935         if (cap = this.rules.hr.exec(src)) {
18936           src = src.substring(cap[0].length);
18937           this.tokens.push({
18938             type: 'hr'
18939           });
18940           continue;
18941         }
18942     
18943         // blockquote
18944         if (cap = this.rules.blockquote.exec(src)) {
18945           src = src.substring(cap[0].length);
18946     
18947           this.tokens.push({
18948             type: 'blockquote_start'
18949           });
18950     
18951           cap = cap[0].replace(/^ *> ?/gm, '');
18952     
18953           // Pass `top` to keep the current
18954           // "toplevel" state. This is exactly
18955           // how markdown.pl works.
18956           this.token(cap, top, true);
18957     
18958           this.tokens.push({
18959             type: 'blockquote_end'
18960           });
18961     
18962           continue;
18963         }
18964     
18965         // list
18966         if (cap = this.rules.list.exec(src)) {
18967           src = src.substring(cap[0].length);
18968           bull = cap[2];
18969     
18970           this.tokens.push({
18971             type: 'list_start',
18972             ordered: bull.length > 1
18973           });
18974     
18975           // Get each top-level item.
18976           cap = cap[0].match(this.rules.item);
18977     
18978           next = false;
18979           l = cap.length;
18980           i = 0;
18981     
18982           for (; i < l; i++) {
18983             item = cap[i];
18984     
18985             // Remove the list item's bullet
18986             // so it is seen as the next token.
18987             space = item.length;
18988             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18989     
18990             // Outdent whatever the
18991             // list item contains. Hacky.
18992             if (~item.indexOf('\n ')) {
18993               space -= item.length;
18994               item = !this.options.pedantic
18995                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18996                 : item.replace(/^ {1,4}/gm, '');
18997             }
18998     
18999             // Determine whether the next list item belongs here.
19000             // Backpedal if it does not belong in this list.
19001             if (this.options.smartLists && i !== l - 1) {
19002               b = block.bullet.exec(cap[i + 1])[0];
19003               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19004                 src = cap.slice(i + 1).join('\n') + src;
19005                 i = l - 1;
19006               }
19007             }
19008     
19009             // Determine whether item is loose or not.
19010             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19011             // for discount behavior.
19012             loose = next || /\n\n(?!\s*$)/.test(item);
19013             if (i !== l - 1) {
19014               next = item.charAt(item.length - 1) === '\n';
19015               if (!loose) { loose = next; }
19016             }
19017     
19018             this.tokens.push({
19019               type: loose
19020                 ? 'loose_item_start'
19021                 : 'list_item_start'
19022             });
19023     
19024             // Recurse.
19025             this.token(item, false, bq);
19026     
19027             this.tokens.push({
19028               type: 'list_item_end'
19029             });
19030           }
19031     
19032           this.tokens.push({
19033             type: 'list_end'
19034           });
19035     
19036           continue;
19037         }
19038     
19039         // html
19040         if (cap = this.rules.html.exec(src)) {
19041           src = src.substring(cap[0].length);
19042           this.tokens.push({
19043             type: this.options.sanitize
19044               ? 'paragraph'
19045               : 'html',
19046             pre: !this.options.sanitizer
19047               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19048             text: cap[0]
19049           });
19050           continue;
19051         }
19052     
19053         // def
19054         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19055           src = src.substring(cap[0].length);
19056           this.tokens.links[cap[1].toLowerCase()] = {
19057             href: cap[2],
19058             title: cap[3]
19059           };
19060           continue;
19061         }
19062     
19063         // table (gfm)
19064         if (top && (cap = this.rules.table.exec(src))) {
19065           src = src.substring(cap[0].length);
19066     
19067           item = {
19068             type: 'table',
19069             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19070             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19071             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19072           };
19073     
19074           for (i = 0; i < item.align.length; i++) {
19075             if (/^ *-+: *$/.test(item.align[i])) {
19076               item.align[i] = 'right';
19077             } else if (/^ *:-+: *$/.test(item.align[i])) {
19078               item.align[i] = 'center';
19079             } else if (/^ *:-+ *$/.test(item.align[i])) {
19080               item.align[i] = 'left';
19081             } else {
19082               item.align[i] = null;
19083             }
19084           }
19085     
19086           for (i = 0; i < item.cells.length; i++) {
19087             item.cells[i] = item.cells[i]
19088               .replace(/^ *\| *| *\| *$/g, '')
19089               .split(/ *\| */);
19090           }
19091     
19092           this.tokens.push(item);
19093     
19094           continue;
19095         }
19096     
19097         // top-level paragraph
19098         if (top && (cap = this.rules.paragraph.exec(src))) {
19099           src = src.substring(cap[0].length);
19100           this.tokens.push({
19101             type: 'paragraph',
19102             text: cap[1].charAt(cap[1].length - 1) === '\n'
19103               ? cap[1].slice(0, -1)
19104               : cap[1]
19105           });
19106           continue;
19107         }
19108     
19109         // text
19110         if (cap = this.rules.text.exec(src)) {
19111           // Top-level should never reach here.
19112           src = src.substring(cap[0].length);
19113           this.tokens.push({
19114             type: 'text',
19115             text: cap[0]
19116           });
19117           continue;
19118         }
19119     
19120         if (src) {
19121           throw new
19122             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19123         }
19124       }
19125     
19126       return this.tokens;
19127     };
19128     
19129     /**
19130      * Inline-Level Grammar
19131      */
19132     
19133     var inline = {
19134       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19135       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19136       url: noop,
19137       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19138       link: /^!?\[(inside)\]\(href\)/,
19139       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19140       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19141       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19142       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19143       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19144       br: /^ {2,}\n(?!\s*$)/,
19145       del: noop,
19146       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19147     };
19148     
19149     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19150     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19151     
19152     inline.link = replace(inline.link)
19153       ('inside', inline._inside)
19154       ('href', inline._href)
19155       ();
19156     
19157     inline.reflink = replace(inline.reflink)
19158       ('inside', inline._inside)
19159       ();
19160     
19161     /**
19162      * Normal Inline Grammar
19163      */
19164     
19165     inline.normal = merge({}, inline);
19166     
19167     /**
19168      * Pedantic Inline Grammar
19169      */
19170     
19171     inline.pedantic = merge({}, inline.normal, {
19172       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19173       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19174     });
19175     
19176     /**
19177      * GFM Inline Grammar
19178      */
19179     
19180     inline.gfm = merge({}, inline.normal, {
19181       escape: replace(inline.escape)('])', '~|])')(),
19182       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19183       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19184       text: replace(inline.text)
19185         (']|', '~]|')
19186         ('|', '|https?://|')
19187         ()
19188     });
19189     
19190     /**
19191      * GFM + Line Breaks Inline Grammar
19192      */
19193     
19194     inline.breaks = merge({}, inline.gfm, {
19195       br: replace(inline.br)('{2,}', '*')(),
19196       text: replace(inline.gfm.text)('{2,}', '*')()
19197     });
19198     
19199     /**
19200      * Inline Lexer & Compiler
19201      */
19202     
19203     var InlineLexer  = function (links, options) {
19204       this.options = options || marked.defaults;
19205       this.links = links;
19206       this.rules = inline.normal;
19207       this.renderer = this.options.renderer || new Renderer;
19208       this.renderer.options = this.options;
19209     
19210       if (!this.links) {
19211         throw new
19212           Error('Tokens array requires a `links` property.');
19213       }
19214     
19215       if (this.options.gfm) {
19216         if (this.options.breaks) {
19217           this.rules = inline.breaks;
19218         } else {
19219           this.rules = inline.gfm;
19220         }
19221       } else if (this.options.pedantic) {
19222         this.rules = inline.pedantic;
19223       }
19224     }
19225     
19226     /**
19227      * Expose Inline Rules
19228      */
19229     
19230     InlineLexer.rules = inline;
19231     
19232     /**
19233      * Static Lexing/Compiling Method
19234      */
19235     
19236     InlineLexer.output = function(src, links, options) {
19237       var inline = new InlineLexer(links, options);
19238       return inline.output(src);
19239     };
19240     
19241     /**
19242      * Lexing/Compiling
19243      */
19244     
19245     InlineLexer.prototype.output = function(src) {
19246       var out = ''
19247         , link
19248         , text
19249         , href
19250         , cap;
19251     
19252       while (src) {
19253         // escape
19254         if (cap = this.rules.escape.exec(src)) {
19255           src = src.substring(cap[0].length);
19256           out += cap[1];
19257           continue;
19258         }
19259     
19260         // autolink
19261         if (cap = this.rules.autolink.exec(src)) {
19262           src = src.substring(cap[0].length);
19263           if (cap[2] === '@') {
19264             text = cap[1].charAt(6) === ':'
19265               ? this.mangle(cap[1].substring(7))
19266               : this.mangle(cap[1]);
19267             href = this.mangle('mailto:') + text;
19268           } else {
19269             text = escape(cap[1]);
19270             href = text;
19271           }
19272           out += this.renderer.link(href, null, text);
19273           continue;
19274         }
19275     
19276         // url (gfm)
19277         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19278           src = src.substring(cap[0].length);
19279           text = escape(cap[1]);
19280           href = text;
19281           out += this.renderer.link(href, null, text);
19282           continue;
19283         }
19284     
19285         // tag
19286         if (cap = this.rules.tag.exec(src)) {
19287           if (!this.inLink && /^<a /i.test(cap[0])) {
19288             this.inLink = true;
19289           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19290             this.inLink = false;
19291           }
19292           src = src.substring(cap[0].length);
19293           out += this.options.sanitize
19294             ? this.options.sanitizer
19295               ? this.options.sanitizer(cap[0])
19296               : escape(cap[0])
19297             : cap[0];
19298           continue;
19299         }
19300     
19301         // link
19302         if (cap = this.rules.link.exec(src)) {
19303           src = src.substring(cap[0].length);
19304           this.inLink = true;
19305           out += this.outputLink(cap, {
19306             href: cap[2],
19307             title: cap[3]
19308           });
19309           this.inLink = false;
19310           continue;
19311         }
19312     
19313         // reflink, nolink
19314         if ((cap = this.rules.reflink.exec(src))
19315             || (cap = this.rules.nolink.exec(src))) {
19316           src = src.substring(cap[0].length);
19317           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19318           link = this.links[link.toLowerCase()];
19319           if (!link || !link.href) {
19320             out += cap[0].charAt(0);
19321             src = cap[0].substring(1) + src;
19322             continue;
19323           }
19324           this.inLink = true;
19325           out += this.outputLink(cap, link);
19326           this.inLink = false;
19327           continue;
19328         }
19329     
19330         // strong
19331         if (cap = this.rules.strong.exec(src)) {
19332           src = src.substring(cap[0].length);
19333           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19334           continue;
19335         }
19336     
19337         // em
19338         if (cap = this.rules.em.exec(src)) {
19339           src = src.substring(cap[0].length);
19340           out += this.renderer.em(this.output(cap[2] || cap[1]));
19341           continue;
19342         }
19343     
19344         // code
19345         if (cap = this.rules.code.exec(src)) {
19346           src = src.substring(cap[0].length);
19347           out += this.renderer.codespan(escape(cap[2], true));
19348           continue;
19349         }
19350     
19351         // br
19352         if (cap = this.rules.br.exec(src)) {
19353           src = src.substring(cap[0].length);
19354           out += this.renderer.br();
19355           continue;
19356         }
19357     
19358         // del (gfm)
19359         if (cap = this.rules.del.exec(src)) {
19360           src = src.substring(cap[0].length);
19361           out += this.renderer.del(this.output(cap[1]));
19362           continue;
19363         }
19364     
19365         // text
19366         if (cap = this.rules.text.exec(src)) {
19367           src = src.substring(cap[0].length);
19368           out += this.renderer.text(escape(this.smartypants(cap[0])));
19369           continue;
19370         }
19371     
19372         if (src) {
19373           throw new
19374             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19375         }
19376       }
19377     
19378       return out;
19379     };
19380     
19381     /**
19382      * Compile Link
19383      */
19384     
19385     InlineLexer.prototype.outputLink = function(cap, link) {
19386       var href = escape(link.href)
19387         , title = link.title ? escape(link.title) : null;
19388     
19389       return cap[0].charAt(0) !== '!'
19390         ? this.renderer.link(href, title, this.output(cap[1]))
19391         : this.renderer.image(href, title, escape(cap[1]));
19392     };
19393     
19394     /**
19395      * Smartypants Transformations
19396      */
19397     
19398     InlineLexer.prototype.smartypants = function(text) {
19399       if (!this.options.smartypants)  { return text; }
19400       return text
19401         // em-dashes
19402         .replace(/---/g, '\u2014')
19403         // en-dashes
19404         .replace(/--/g, '\u2013')
19405         // opening singles
19406         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19407         // closing singles & apostrophes
19408         .replace(/'/g, '\u2019')
19409         // opening doubles
19410         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19411         // closing doubles
19412         .replace(/"/g, '\u201d')
19413         // ellipses
19414         .replace(/\.{3}/g, '\u2026');
19415     };
19416     
19417     /**
19418      * Mangle Links
19419      */
19420     
19421     InlineLexer.prototype.mangle = function(text) {
19422       if (!this.options.mangle) { return text; }
19423       var out = ''
19424         , l = text.length
19425         , i = 0
19426         , ch;
19427     
19428       for (; i < l; i++) {
19429         ch = text.charCodeAt(i);
19430         if (Math.random() > 0.5) {
19431           ch = 'x' + ch.toString(16);
19432         }
19433         out += '&#' + ch + ';';
19434       }
19435     
19436       return out;
19437     };
19438     
19439     /**
19440      * Renderer
19441      */
19442     
19443      /**
19444          * eval:var:Renderer
19445     */
19446     
19447     var Renderer   = function (options) {
19448       this.options = options || {};
19449     }
19450     
19451     Renderer.prototype.code = function(code, lang, escaped) {
19452       if (this.options.highlight) {
19453         var out = this.options.highlight(code, lang);
19454         if (out != null && out !== code) {
19455           escaped = true;
19456           code = out;
19457         }
19458       } else {
19459             // hack!!! - it's already escapeD?
19460             escaped = true;
19461       }
19462     
19463       if (!lang) {
19464         return '<pre><code>'
19465           + (escaped ? code : escape(code, true))
19466           + '\n</code></pre>';
19467       }
19468     
19469       return '<pre><code class="'
19470         + this.options.langPrefix
19471         + escape(lang, true)
19472         + '">'
19473         + (escaped ? code : escape(code, true))
19474         + '\n</code></pre>\n';
19475     };
19476     
19477     Renderer.prototype.blockquote = function(quote) {
19478       return '<blockquote>\n' + quote + '</blockquote>\n';
19479     };
19480     
19481     Renderer.prototype.html = function(html) {
19482       return html;
19483     };
19484     
19485     Renderer.prototype.heading = function(text, level, raw) {
19486       return '<h'
19487         + level
19488         + ' id="'
19489         + this.options.headerPrefix
19490         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19491         + '">'
19492         + text
19493         + '</h'
19494         + level
19495         + '>\n';
19496     };
19497     
19498     Renderer.prototype.hr = function() {
19499       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19500     };
19501     
19502     Renderer.prototype.list = function(body, ordered) {
19503       var type = ordered ? 'ol' : 'ul';
19504       return '<' + type + '>\n' + body + '</' + type + '>\n';
19505     };
19506     
19507     Renderer.prototype.listitem = function(text) {
19508       return '<li>' + text + '</li>\n';
19509     };
19510     
19511     Renderer.prototype.paragraph = function(text) {
19512       return '<p>' + text + '</p>\n';
19513     };
19514     
19515     Renderer.prototype.table = function(header, body) {
19516       return '<table class="table table-striped">\n'
19517         + '<thead>\n'
19518         + header
19519         + '</thead>\n'
19520         + '<tbody>\n'
19521         + body
19522         + '</tbody>\n'
19523         + '</table>\n';
19524     };
19525     
19526     Renderer.prototype.tablerow = function(content) {
19527       return '<tr>\n' + content + '</tr>\n';
19528     };
19529     
19530     Renderer.prototype.tablecell = function(content, flags) {
19531       var type = flags.header ? 'th' : 'td';
19532       var tag = flags.align
19533         ? '<' + type + ' style="text-align:' + flags.align + '">'
19534         : '<' + type + '>';
19535       return tag + content + '</' + type + '>\n';
19536     };
19537     
19538     // span level renderer
19539     Renderer.prototype.strong = function(text) {
19540       return '<strong>' + text + '</strong>';
19541     };
19542     
19543     Renderer.prototype.em = function(text) {
19544       return '<em>' + text + '</em>';
19545     };
19546     
19547     Renderer.prototype.codespan = function(text) {
19548       return '<code>' + text + '</code>';
19549     };
19550     
19551     Renderer.prototype.br = function() {
19552       return this.options.xhtml ? '<br/>' : '<br>';
19553     };
19554     
19555     Renderer.prototype.del = function(text) {
19556       return '<del>' + text + '</del>';
19557     };
19558     
19559     Renderer.prototype.link = function(href, title, text) {
19560       if (this.options.sanitize) {
19561         try {
19562           var prot = decodeURIComponent(unescape(href))
19563             .replace(/[^\w:]/g, '')
19564             .toLowerCase();
19565         } catch (e) {
19566           return '';
19567         }
19568         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19569           return '';
19570         }
19571       }
19572       var out = '<a href="' + href + '"';
19573       if (title) {
19574         out += ' title="' + title + '"';
19575       }
19576       out += '>' + text + '</a>';
19577       return out;
19578     };
19579     
19580     Renderer.prototype.image = function(href, title, text) {
19581       var out = '<img src="' + href + '" alt="' + text + '"';
19582       if (title) {
19583         out += ' title="' + title + '"';
19584       }
19585       out += this.options.xhtml ? '/>' : '>';
19586       return out;
19587     };
19588     
19589     Renderer.prototype.text = function(text) {
19590       return text;
19591     };
19592     
19593     /**
19594      * Parsing & Compiling
19595      */
19596          /**
19597          * eval:var:Parser
19598     */
19599     
19600     var Parser= function (options) {
19601       this.tokens = [];
19602       this.token = null;
19603       this.options = options || marked.defaults;
19604       this.options.renderer = this.options.renderer || new Renderer;
19605       this.renderer = this.options.renderer;
19606       this.renderer.options = this.options;
19607     }
19608     
19609     /**
19610      * Static Parse Method
19611      */
19612     
19613     Parser.parse = function(src, options, renderer) {
19614       var parser = new Parser(options, renderer);
19615       return parser.parse(src);
19616     };
19617     
19618     /**
19619      * Parse Loop
19620      */
19621     
19622     Parser.prototype.parse = function(src) {
19623       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19624       this.tokens = src.reverse();
19625     
19626       var out = '';
19627       while (this.next()) {
19628         out += this.tok();
19629       }
19630     
19631       return out;
19632     };
19633     
19634     /**
19635      * Next Token
19636      */
19637     
19638     Parser.prototype.next = function() {
19639       return this.token = this.tokens.pop();
19640     };
19641     
19642     /**
19643      * Preview Next Token
19644      */
19645     
19646     Parser.prototype.peek = function() {
19647       return this.tokens[this.tokens.length - 1] || 0;
19648     };
19649     
19650     /**
19651      * Parse Text Tokens
19652      */
19653     
19654     Parser.prototype.parseText = function() {
19655       var body = this.token.text;
19656     
19657       while (this.peek().type === 'text') {
19658         body += '\n' + this.next().text;
19659       }
19660     
19661       return this.inline.output(body);
19662     };
19663     
19664     /**
19665      * Parse Current Token
19666      */
19667     
19668     Parser.prototype.tok = function() {
19669       switch (this.token.type) {
19670         case 'space': {
19671           return '';
19672         }
19673         case 'hr': {
19674           return this.renderer.hr();
19675         }
19676         case 'heading': {
19677           return this.renderer.heading(
19678             this.inline.output(this.token.text),
19679             this.token.depth,
19680             this.token.text);
19681         }
19682         case 'code': {
19683           return this.renderer.code(this.token.text,
19684             this.token.lang,
19685             this.token.escaped);
19686         }
19687         case 'table': {
19688           var header = ''
19689             , body = ''
19690             , i
19691             , row
19692             , cell
19693             , flags
19694             , j;
19695     
19696           // header
19697           cell = '';
19698           for (i = 0; i < this.token.header.length; i++) {
19699             flags = { header: true, align: this.token.align[i] };
19700             cell += this.renderer.tablecell(
19701               this.inline.output(this.token.header[i]),
19702               { header: true, align: this.token.align[i] }
19703             );
19704           }
19705           header += this.renderer.tablerow(cell);
19706     
19707           for (i = 0; i < this.token.cells.length; i++) {
19708             row = this.token.cells[i];
19709     
19710             cell = '';
19711             for (j = 0; j < row.length; j++) {
19712               cell += this.renderer.tablecell(
19713                 this.inline.output(row[j]),
19714                 { header: false, align: this.token.align[j] }
19715               );
19716             }
19717     
19718             body += this.renderer.tablerow(cell);
19719           }
19720           return this.renderer.table(header, body);
19721         }
19722         case 'blockquote_start': {
19723           var body = '';
19724     
19725           while (this.next().type !== 'blockquote_end') {
19726             body += this.tok();
19727           }
19728     
19729           return this.renderer.blockquote(body);
19730         }
19731         case 'list_start': {
19732           var body = ''
19733             , ordered = this.token.ordered;
19734     
19735           while (this.next().type !== 'list_end') {
19736             body += this.tok();
19737           }
19738     
19739           return this.renderer.list(body, ordered);
19740         }
19741         case 'list_item_start': {
19742           var body = '';
19743     
19744           while (this.next().type !== 'list_item_end') {
19745             body += this.token.type === 'text'
19746               ? this.parseText()
19747               : this.tok();
19748           }
19749     
19750           return this.renderer.listitem(body);
19751         }
19752         case 'loose_item_start': {
19753           var body = '';
19754     
19755           while (this.next().type !== 'list_item_end') {
19756             body += this.tok();
19757           }
19758     
19759           return this.renderer.listitem(body);
19760         }
19761         case 'html': {
19762           var html = !this.token.pre && !this.options.pedantic
19763             ? this.inline.output(this.token.text)
19764             : this.token.text;
19765           return this.renderer.html(html);
19766         }
19767         case 'paragraph': {
19768           return this.renderer.paragraph(this.inline.output(this.token.text));
19769         }
19770         case 'text': {
19771           return this.renderer.paragraph(this.parseText());
19772         }
19773       }
19774     };
19775   
19776     
19777     /**
19778      * Marked
19779      */
19780          /**
19781          * eval:var:marked
19782     */
19783     var marked = function (src, opt, callback) {
19784       if (callback || typeof opt === 'function') {
19785         if (!callback) {
19786           callback = opt;
19787           opt = null;
19788         }
19789     
19790         opt = merge({}, marked.defaults, opt || {});
19791     
19792         var highlight = opt.highlight
19793           , tokens
19794           , pending
19795           , i = 0;
19796     
19797         try {
19798           tokens = Lexer.lex(src, opt)
19799         } catch (e) {
19800           return callback(e);
19801         }
19802     
19803         pending = tokens.length;
19804          /**
19805          * eval:var:done
19806     */
19807         var done = function(err) {
19808           if (err) {
19809             opt.highlight = highlight;
19810             return callback(err);
19811           }
19812     
19813           var out;
19814     
19815           try {
19816             out = Parser.parse(tokens, opt);
19817           } catch (e) {
19818             err = e;
19819           }
19820     
19821           opt.highlight = highlight;
19822     
19823           return err
19824             ? callback(err)
19825             : callback(null, out);
19826         };
19827     
19828         if (!highlight || highlight.length < 3) {
19829           return done();
19830         }
19831     
19832         delete opt.highlight;
19833     
19834         if (!pending) { return done(); }
19835     
19836         for (; i < tokens.length; i++) {
19837           (function(token) {
19838             if (token.type !== 'code') {
19839               return --pending || done();
19840             }
19841             return highlight(token.text, token.lang, function(err, code) {
19842               if (err) { return done(err); }
19843               if (code == null || code === token.text) {
19844                 return --pending || done();
19845               }
19846               token.text = code;
19847               token.escaped = true;
19848               --pending || done();
19849             });
19850           })(tokens[i]);
19851         }
19852     
19853         return;
19854       }
19855       try {
19856         if (opt) { opt = merge({}, marked.defaults, opt); }
19857         return Parser.parse(Lexer.lex(src, opt), opt);
19858       } catch (e) {
19859         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19860         if ((opt || marked.defaults).silent) {
19861           return '<p>An error occured:</p><pre>'
19862             + escape(e.message + '', true)
19863             + '</pre>';
19864         }
19865         throw e;
19866       }
19867     }
19868     
19869     /**
19870      * Options
19871      */
19872     
19873     marked.options =
19874     marked.setOptions = function(opt) {
19875       merge(marked.defaults, opt);
19876       return marked;
19877     };
19878     
19879     marked.defaults = {
19880       gfm: true,
19881       tables: true,
19882       breaks: false,
19883       pedantic: false,
19884       sanitize: false,
19885       sanitizer: null,
19886       mangle: true,
19887       smartLists: false,
19888       silent: false,
19889       highlight: null,
19890       langPrefix: 'lang-',
19891       smartypants: false,
19892       headerPrefix: '',
19893       renderer: new Renderer,
19894       xhtml: false
19895     };
19896     
19897     /**
19898      * Expose
19899      */
19900     
19901     marked.Parser = Parser;
19902     marked.parser = Parser.parse;
19903     
19904     marked.Renderer = Renderer;
19905     
19906     marked.Lexer = Lexer;
19907     marked.lexer = Lexer.lex;
19908     
19909     marked.InlineLexer = InlineLexer;
19910     marked.inlineLexer = InlineLexer.output;
19911     
19912     marked.parse = marked;
19913     
19914     Roo.Markdown.marked = marked;
19915
19916 })();/*
19917  * Based on:
19918  * Ext JS Library 1.1.1
19919  * Copyright(c) 2006-2007, Ext JS, LLC.
19920  *
19921  * Originally Released Under LGPL - original licence link has changed is not relivant.
19922  *
19923  * Fork - LGPL
19924  * <script type="text/javascript">
19925  */
19926
19927
19928
19929 /*
19930  * These classes are derivatives of the similarly named classes in the YUI Library.
19931  * The original license:
19932  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19933  * Code licensed under the BSD License:
19934  * http://developer.yahoo.net/yui/license.txt
19935  */
19936
19937 (function() {
19938
19939 var Event=Roo.EventManager;
19940 var Dom=Roo.lib.Dom;
19941
19942 /**
19943  * @class Roo.dd.DragDrop
19944  * @extends Roo.util.Observable
19945  * Defines the interface and base operation of items that that can be
19946  * dragged or can be drop targets.  It was designed to be extended, overriding
19947  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19948  * Up to three html elements can be associated with a DragDrop instance:
19949  * <ul>
19950  * <li>linked element: the element that is passed into the constructor.
19951  * This is the element which defines the boundaries for interaction with
19952  * other DragDrop objects.</li>
19953  * <li>handle element(s): The drag operation only occurs if the element that
19954  * was clicked matches a handle element.  By default this is the linked
19955  * element, but there are times that you will want only a portion of the
19956  * linked element to initiate the drag operation, and the setHandleElId()
19957  * method provides a way to define this.</li>
19958  * <li>drag element: this represents the element that would be moved along
19959  * with the cursor during a drag operation.  By default, this is the linked
19960  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19961  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19962  * </li>
19963  * </ul>
19964  * This class should not be instantiated until the onload event to ensure that
19965  * the associated elements are available.
19966  * The following would define a DragDrop obj that would interact with any
19967  * other DragDrop obj in the "group1" group:
19968  * <pre>
19969  *  dd = new Roo.dd.DragDrop("div1", "group1");
19970  * </pre>
19971  * Since none of the event handlers have been implemented, nothing would
19972  * actually happen if you were to run the code above.  Normally you would
19973  * override this class or one of the default implementations, but you can
19974  * also override the methods you want on an instance of the class...
19975  * <pre>
19976  *  dd.onDragDrop = function(e, id) {
19977  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19978  *  }
19979  * </pre>
19980  * @constructor
19981  * @param {String} id of the element that is linked to this instance
19982  * @param {String} sGroup the group of related DragDrop objects
19983  * @param {object} config an object containing configurable attributes
19984  *                Valid properties for DragDrop:
19985  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19986  */
19987 Roo.dd.DragDrop = function(id, sGroup, config) {
19988     if (id) {
19989         this.init(id, sGroup, config);
19990     }
19991     
19992 };
19993
19994 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19995
19996     /**
19997      * The id of the element associated with this object.  This is what we
19998      * refer to as the "linked element" because the size and position of
19999      * this element is used to determine when the drag and drop objects have
20000      * interacted.
20001      * @property id
20002      * @type String
20003      */
20004     id: null,
20005
20006     /**
20007      * Configuration attributes passed into the constructor
20008      * @property config
20009      * @type object
20010      */
20011     config: null,
20012
20013     /**
20014      * The id of the element that will be dragged.  By default this is same
20015      * as the linked element , but could be changed to another element. Ex:
20016      * Roo.dd.DDProxy
20017      * @property dragElId
20018      * @type String
20019      * @private
20020      */
20021     dragElId: null,
20022
20023     /**
20024      * the id of the element that initiates the drag operation.  By default
20025      * this is the linked element, but could be changed to be a child of this
20026      * element.  This lets us do things like only starting the drag when the
20027      * header element within the linked html element is clicked.
20028      * @property handleElId
20029      * @type String
20030      * @private
20031      */
20032     handleElId: null,
20033
20034     /**
20035      * An associative array of HTML tags that will be ignored if clicked.
20036      * @property invalidHandleTypes
20037      * @type {string: string}
20038      */
20039     invalidHandleTypes: null,
20040
20041     /**
20042      * An associative array of ids for elements that will be ignored if clicked
20043      * @property invalidHandleIds
20044      * @type {string: string}
20045      */
20046     invalidHandleIds: null,
20047
20048     /**
20049      * An indexted array of css class names for elements that will be ignored
20050      * if clicked.
20051      * @property invalidHandleClasses
20052      * @type string[]
20053      */
20054     invalidHandleClasses: null,
20055
20056     /**
20057      * The linked element's absolute X position at the time the drag was
20058      * started
20059      * @property startPageX
20060      * @type int
20061      * @private
20062      */
20063     startPageX: 0,
20064
20065     /**
20066      * The linked element's absolute X position at the time the drag was
20067      * started
20068      * @property startPageY
20069      * @type int
20070      * @private
20071      */
20072     startPageY: 0,
20073
20074     /**
20075      * The group defines a logical collection of DragDrop objects that are
20076      * related.  Instances only get events when interacting with other
20077      * DragDrop object in the same group.  This lets us define multiple
20078      * groups using a single DragDrop subclass if we want.
20079      * @property groups
20080      * @type {string: string}
20081      */
20082     groups: null,
20083
20084     /**
20085      * Individual drag/drop instances can be locked.  This will prevent
20086      * onmousedown start drag.
20087      * @property locked
20088      * @type boolean
20089      * @private
20090      */
20091     locked: false,
20092
20093     /**
20094      * Lock this instance
20095      * @method lock
20096      */
20097     lock: function() { this.locked = true; },
20098
20099     /**
20100      * Unlock this instace
20101      * @method unlock
20102      */
20103     unlock: function() { this.locked = false; },
20104
20105     /**
20106      * By default, all insances can be a drop target.  This can be disabled by
20107      * setting isTarget to false.
20108      * @method isTarget
20109      * @type boolean
20110      */
20111     isTarget: true,
20112
20113     /**
20114      * The padding configured for this drag and drop object for calculating
20115      * the drop zone intersection with this object.
20116      * @method padding
20117      * @type int[]
20118      */
20119     padding: null,
20120
20121     /**
20122      * Cached reference to the linked element
20123      * @property _domRef
20124      * @private
20125      */
20126     _domRef: null,
20127
20128     /**
20129      * Internal typeof flag
20130      * @property __ygDragDrop
20131      * @private
20132      */
20133     __ygDragDrop: true,
20134
20135     /**
20136      * Set to true when horizontal contraints are applied
20137      * @property constrainX
20138      * @type boolean
20139      * @private
20140      */
20141     constrainX: false,
20142
20143     /**
20144      * Set to true when vertical contraints are applied
20145      * @property constrainY
20146      * @type boolean
20147      * @private
20148      */
20149     constrainY: false,
20150
20151     /**
20152      * The left constraint
20153      * @property minX
20154      * @type int
20155      * @private
20156      */
20157     minX: 0,
20158
20159     /**
20160      * The right constraint
20161      * @property maxX
20162      * @type int
20163      * @private
20164      */
20165     maxX: 0,
20166
20167     /**
20168      * The up constraint
20169      * @property minY
20170      * @type int
20171      * @type int
20172      * @private
20173      */
20174     minY: 0,
20175
20176     /**
20177      * The down constraint
20178      * @property maxY
20179      * @type int
20180      * @private
20181      */
20182     maxY: 0,
20183
20184     /**
20185      * Maintain offsets when we resetconstraints.  Set to true when you want
20186      * the position of the element relative to its parent to stay the same
20187      * when the page changes
20188      *
20189      * @property maintainOffset
20190      * @type boolean
20191      */
20192     maintainOffset: false,
20193
20194     /**
20195      * Array of pixel locations the element will snap to if we specified a
20196      * horizontal graduation/interval.  This array is generated automatically
20197      * when you define a tick interval.
20198      * @property xTicks
20199      * @type int[]
20200      */
20201     xTicks: null,
20202
20203     /**
20204      * Array of pixel locations the element will snap to if we specified a
20205      * vertical graduation/interval.  This array is generated automatically
20206      * when you define a tick interval.
20207      * @property yTicks
20208      * @type int[]
20209      */
20210     yTicks: null,
20211
20212     /**
20213      * By default the drag and drop instance will only respond to the primary
20214      * button click (left button for a right-handed mouse).  Set to true to
20215      * allow drag and drop to start with any mouse click that is propogated
20216      * by the browser
20217      * @property primaryButtonOnly
20218      * @type boolean
20219      */
20220     primaryButtonOnly: true,
20221
20222     /**
20223      * The availabe property is false until the linked dom element is accessible.
20224      * @property available
20225      * @type boolean
20226      */
20227     available: false,
20228
20229     /**
20230      * By default, drags can only be initiated if the mousedown occurs in the
20231      * region the linked element is.  This is done in part to work around a
20232      * bug in some browsers that mis-report the mousedown if the previous
20233      * mouseup happened outside of the window.  This property is set to true
20234      * if outer handles are defined.
20235      *
20236      * @property hasOuterHandles
20237      * @type boolean
20238      * @default false
20239      */
20240     hasOuterHandles: false,
20241
20242     /**
20243      * Code that executes immediately before the startDrag event
20244      * @method b4StartDrag
20245      * @private
20246      */
20247     b4StartDrag: function(x, y) { },
20248
20249     /**
20250      * Abstract method called after a drag/drop object is clicked
20251      * and the drag or mousedown time thresholds have beeen met.
20252      * @method startDrag
20253      * @param {int} X click location
20254      * @param {int} Y click location
20255      */
20256     startDrag: function(x, y) { /* override this */ },
20257
20258     /**
20259      * Code that executes immediately before the onDrag event
20260      * @method b4Drag
20261      * @private
20262      */
20263     b4Drag: function(e) { },
20264
20265     /**
20266      * Abstract method called during the onMouseMove event while dragging an
20267      * object.
20268      * @method onDrag
20269      * @param {Event} e the mousemove event
20270      */
20271     onDrag: function(e) { /* override this */ },
20272
20273     /**
20274      * Abstract method called when this element fist begins hovering over
20275      * another DragDrop obj
20276      * @method onDragEnter
20277      * @param {Event} e the mousemove event
20278      * @param {String|DragDrop[]} id In POINT mode, the element
20279      * id this is hovering over.  In INTERSECT mode, an array of one or more
20280      * dragdrop items being hovered over.
20281      */
20282     onDragEnter: function(e, id) { /* override this */ },
20283
20284     /**
20285      * Code that executes immediately before the onDragOver event
20286      * @method b4DragOver
20287      * @private
20288      */
20289     b4DragOver: function(e) { },
20290
20291     /**
20292      * Abstract method called when this element is hovering over another
20293      * DragDrop obj
20294      * @method onDragOver
20295      * @param {Event} e the mousemove event
20296      * @param {String|DragDrop[]} id In POINT mode, the element
20297      * id this is hovering over.  In INTERSECT mode, an array of dd items
20298      * being hovered over.
20299      */
20300     onDragOver: function(e, id) { /* override this */ },
20301
20302     /**
20303      * Code that executes immediately before the onDragOut event
20304      * @method b4DragOut
20305      * @private
20306      */
20307     b4DragOut: function(e) { },
20308
20309     /**
20310      * Abstract method called when we are no longer hovering over an element
20311      * @method onDragOut
20312      * @param {Event} e the mousemove event
20313      * @param {String|DragDrop[]} id In POINT mode, the element
20314      * id this was hovering over.  In INTERSECT mode, an array of dd items
20315      * that the mouse is no longer over.
20316      */
20317     onDragOut: function(e, id) { /* override this */ },
20318
20319     /**
20320      * Code that executes immediately before the onDragDrop event
20321      * @method b4DragDrop
20322      * @private
20323      */
20324     b4DragDrop: function(e) { },
20325
20326     /**
20327      * Abstract method called when this item is dropped on another DragDrop
20328      * obj
20329      * @method onDragDrop
20330      * @param {Event} e the mouseup event
20331      * @param {String|DragDrop[]} id In POINT mode, the element
20332      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20333      * was dropped on.
20334      */
20335     onDragDrop: function(e, id) { /* override this */ },
20336
20337     /**
20338      * Abstract method called when this item is dropped on an area with no
20339      * drop target
20340      * @method onInvalidDrop
20341      * @param {Event} e the mouseup event
20342      */
20343     onInvalidDrop: function(e) { /* override this */ },
20344
20345     /**
20346      * Code that executes immediately before the endDrag event
20347      * @method b4EndDrag
20348      * @private
20349      */
20350     b4EndDrag: function(e) { },
20351
20352     /**
20353      * Fired when we are done dragging the object
20354      * @method endDrag
20355      * @param {Event} e the mouseup event
20356      */
20357     endDrag: function(e) { /* override this */ },
20358
20359     /**
20360      * Code executed immediately before the onMouseDown event
20361      * @method b4MouseDown
20362      * @param {Event} e the mousedown event
20363      * @private
20364      */
20365     b4MouseDown: function(e) {  },
20366
20367     /**
20368      * Event handler that fires when a drag/drop obj gets a mousedown
20369      * @method onMouseDown
20370      * @param {Event} e the mousedown event
20371      */
20372     onMouseDown: function(e) { /* override this */ },
20373
20374     /**
20375      * Event handler that fires when a drag/drop obj gets a mouseup
20376      * @method onMouseUp
20377      * @param {Event} e the mouseup event
20378      */
20379     onMouseUp: function(e) { /* override this */ },
20380
20381     /**
20382      * Override the onAvailable method to do what is needed after the initial
20383      * position was determined.
20384      * @method onAvailable
20385      */
20386     onAvailable: function () {
20387     },
20388
20389     /*
20390      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20391      * @type Object
20392      */
20393     defaultPadding : {left:0, right:0, top:0, bottom:0},
20394
20395     /*
20396      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20397  *
20398  * Usage:
20399  <pre><code>
20400  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20401                 { dragElId: "existingProxyDiv" });
20402  dd.startDrag = function(){
20403      this.constrainTo("parent-id");
20404  };
20405  </code></pre>
20406  * Or you can initalize it using the {@link Roo.Element} object:
20407  <pre><code>
20408  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20409      startDrag : function(){
20410          this.constrainTo("parent-id");
20411      }
20412  });
20413  </code></pre>
20414      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20415      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20416      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20417      * an object containing the sides to pad. For example: {right:10, bottom:10}
20418      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20419      */
20420     constrainTo : function(constrainTo, pad, inContent){
20421         if(typeof pad == "number"){
20422             pad = {left: pad, right:pad, top:pad, bottom:pad};
20423         }
20424         pad = pad || this.defaultPadding;
20425         var b = Roo.get(this.getEl()).getBox();
20426         var ce = Roo.get(constrainTo);
20427         var s = ce.getScroll();
20428         var c, cd = ce.dom;
20429         if(cd == document.body){
20430             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20431         }else{
20432             xy = ce.getXY();
20433             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20434         }
20435
20436
20437         var topSpace = b.y - c.y;
20438         var leftSpace = b.x - c.x;
20439
20440         this.resetConstraints();
20441         this.setXConstraint(leftSpace - (pad.left||0), // left
20442                 c.width - leftSpace - b.width - (pad.right||0) //right
20443         );
20444         this.setYConstraint(topSpace - (pad.top||0), //top
20445                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20446         );
20447     },
20448
20449     /**
20450      * Returns a reference to the linked element
20451      * @method getEl
20452      * @return {HTMLElement} the html element
20453      */
20454     getEl: function() {
20455         if (!this._domRef) {
20456             this._domRef = Roo.getDom(this.id);
20457         }
20458
20459         return this._domRef;
20460     },
20461
20462     /**
20463      * Returns a reference to the actual element to drag.  By default this is
20464      * the same as the html element, but it can be assigned to another
20465      * element. An example of this can be found in Roo.dd.DDProxy
20466      * @method getDragEl
20467      * @return {HTMLElement} the html element
20468      */
20469     getDragEl: function() {
20470         return Roo.getDom(this.dragElId);
20471     },
20472
20473     /**
20474      * Sets up the DragDrop object.  Must be called in the constructor of any
20475      * Roo.dd.DragDrop subclass
20476      * @method init
20477      * @param id the id of the linked element
20478      * @param {String} sGroup the group of related items
20479      * @param {object} config configuration attributes
20480      */
20481     init: function(id, sGroup, config) {
20482         this.initTarget(id, sGroup, config);
20483         if (!Roo.isTouch) {
20484             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20485         }
20486         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20487         // Event.on(this.id, "selectstart", Event.preventDefault);
20488     },
20489
20490     /**
20491      * Initializes Targeting functionality only... the object does not
20492      * get a mousedown handler.
20493      * @method initTarget
20494      * @param id the id of the linked element
20495      * @param {String} sGroup the group of related items
20496      * @param {object} config configuration attributes
20497      */
20498     initTarget: function(id, sGroup, config) {
20499
20500         // configuration attributes
20501         this.config = config || {};
20502
20503         // create a local reference to the drag and drop manager
20504         this.DDM = Roo.dd.DDM;
20505         // initialize the groups array
20506         this.groups = {};
20507
20508         // assume that we have an element reference instead of an id if the
20509         // parameter is not a string
20510         if (typeof id !== "string") {
20511             id = Roo.id(id);
20512         }
20513
20514         // set the id
20515         this.id = id;
20516
20517         // add to an interaction group
20518         this.addToGroup((sGroup) ? sGroup : "default");
20519
20520         // We don't want to register this as the handle with the manager
20521         // so we just set the id rather than calling the setter.
20522         this.handleElId = id;
20523
20524         // the linked element is the element that gets dragged by default
20525         this.setDragElId(id);
20526
20527         // by default, clicked anchors will not start drag operations.
20528         this.invalidHandleTypes = { A: "A" };
20529         this.invalidHandleIds = {};
20530         this.invalidHandleClasses = [];
20531
20532         this.applyConfig();
20533
20534         this.handleOnAvailable();
20535     },
20536
20537     /**
20538      * Applies the configuration parameters that were passed into the constructor.
20539      * This is supposed to happen at each level through the inheritance chain.  So
20540      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20541      * DragDrop in order to get all of the parameters that are available in
20542      * each object.
20543      * @method applyConfig
20544      */
20545     applyConfig: function() {
20546
20547         // configurable properties:
20548         //    padding, isTarget, maintainOffset, primaryButtonOnly
20549         this.padding           = this.config.padding || [0, 0, 0, 0];
20550         this.isTarget          = (this.config.isTarget !== false);
20551         this.maintainOffset    = (this.config.maintainOffset);
20552         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20553
20554     },
20555
20556     /**
20557      * Executed when the linked element is available
20558      * @method handleOnAvailable
20559      * @private
20560      */
20561     handleOnAvailable: function() {
20562         this.available = true;
20563         this.resetConstraints();
20564         this.onAvailable();
20565     },
20566
20567      /**
20568      * Configures the padding for the target zone in px.  Effectively expands
20569      * (or reduces) the virtual object size for targeting calculations.
20570      * Supports css-style shorthand; if only one parameter is passed, all sides
20571      * will have that padding, and if only two are passed, the top and bottom
20572      * will have the first param, the left and right the second.
20573      * @method setPadding
20574      * @param {int} iTop    Top pad
20575      * @param {int} iRight  Right pad
20576      * @param {int} iBot    Bot pad
20577      * @param {int} iLeft   Left pad
20578      */
20579     setPadding: function(iTop, iRight, iBot, iLeft) {
20580         // this.padding = [iLeft, iRight, iTop, iBot];
20581         if (!iRight && 0 !== iRight) {
20582             this.padding = [iTop, iTop, iTop, iTop];
20583         } else if (!iBot && 0 !== iBot) {
20584             this.padding = [iTop, iRight, iTop, iRight];
20585         } else {
20586             this.padding = [iTop, iRight, iBot, iLeft];
20587         }
20588     },
20589
20590     /**
20591      * Stores the initial placement of the linked element.
20592      * @method setInitialPosition
20593      * @param {int} diffX   the X offset, default 0
20594      * @param {int} diffY   the Y offset, default 0
20595      */
20596     setInitPosition: function(diffX, diffY) {
20597         var el = this.getEl();
20598
20599         if (!this.DDM.verifyEl(el)) {
20600             return;
20601         }
20602
20603         var dx = diffX || 0;
20604         var dy = diffY || 0;
20605
20606         var p = Dom.getXY( el );
20607
20608         this.initPageX = p[0] - dx;
20609         this.initPageY = p[1] - dy;
20610
20611         this.lastPageX = p[0];
20612         this.lastPageY = p[1];
20613
20614
20615         this.setStartPosition(p);
20616     },
20617
20618     /**
20619      * Sets the start position of the element.  This is set when the obj
20620      * is initialized, the reset when a drag is started.
20621      * @method setStartPosition
20622      * @param pos current position (from previous lookup)
20623      * @private
20624      */
20625     setStartPosition: function(pos) {
20626         var p = pos || Dom.getXY( this.getEl() );
20627         this.deltaSetXY = null;
20628
20629         this.startPageX = p[0];
20630         this.startPageY = p[1];
20631     },
20632
20633     /**
20634      * Add this instance to a group of related drag/drop objects.  All
20635      * instances belong to at least one group, and can belong to as many
20636      * groups as needed.
20637      * @method addToGroup
20638      * @param sGroup {string} the name of the group
20639      */
20640     addToGroup: function(sGroup) {
20641         this.groups[sGroup] = true;
20642         this.DDM.regDragDrop(this, sGroup);
20643     },
20644
20645     /**
20646      * Remove's this instance from the supplied interaction group
20647      * @method removeFromGroup
20648      * @param {string}  sGroup  The group to drop
20649      */
20650     removeFromGroup: function(sGroup) {
20651         if (this.groups[sGroup]) {
20652             delete this.groups[sGroup];
20653         }
20654
20655         this.DDM.removeDDFromGroup(this, sGroup);
20656     },
20657
20658     /**
20659      * Allows you to specify that an element other than the linked element
20660      * will be moved with the cursor during a drag
20661      * @method setDragElId
20662      * @param id {string} the id of the element that will be used to initiate the drag
20663      */
20664     setDragElId: function(id) {
20665         this.dragElId = id;
20666     },
20667
20668     /**
20669      * Allows you to specify a child of the linked element that should be
20670      * used to initiate the drag operation.  An example of this would be if
20671      * you have a content div with text and links.  Clicking anywhere in the
20672      * content area would normally start the drag operation.  Use this method
20673      * to specify that an element inside of the content div is the element
20674      * that starts the drag operation.
20675      * @method setHandleElId
20676      * @param id {string} the id of the element that will be used to
20677      * initiate the drag.
20678      */
20679     setHandleElId: function(id) {
20680         if (typeof id !== "string") {
20681             id = Roo.id(id);
20682         }
20683         this.handleElId = id;
20684         this.DDM.regHandle(this.id, id);
20685     },
20686
20687     /**
20688      * Allows you to set an element outside of the linked element as a drag
20689      * handle
20690      * @method setOuterHandleElId
20691      * @param id the id of the element that will be used to initiate the drag
20692      */
20693     setOuterHandleElId: function(id) {
20694         if (typeof id !== "string") {
20695             id = Roo.id(id);
20696         }
20697         Event.on(id, "mousedown",
20698                 this.handleMouseDown, this);
20699         this.setHandleElId(id);
20700
20701         this.hasOuterHandles = true;
20702     },
20703
20704     /**
20705      * Remove all drag and drop hooks for this element
20706      * @method unreg
20707      */
20708     unreg: function() {
20709         Event.un(this.id, "mousedown",
20710                 this.handleMouseDown);
20711         Event.un(this.id, "touchstart",
20712                 this.handleMouseDown);
20713         this._domRef = null;
20714         this.DDM._remove(this);
20715     },
20716
20717     destroy : function(){
20718         this.unreg();
20719     },
20720
20721     /**
20722      * Returns true if this instance is locked, or the drag drop mgr is locked
20723      * (meaning that all drag/drop is disabled on the page.)
20724      * @method isLocked
20725      * @return {boolean} true if this obj or all drag/drop is locked, else
20726      * false
20727      */
20728     isLocked: function() {
20729         return (this.DDM.isLocked() || this.locked);
20730     },
20731
20732     /**
20733      * Fired when this object is clicked
20734      * @method handleMouseDown
20735      * @param {Event} e
20736      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20737      * @private
20738      */
20739     handleMouseDown: function(e, oDD){
20740      
20741         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20742             //Roo.log('not touch/ button !=0');
20743             return;
20744         }
20745         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20746             return; // double touch..
20747         }
20748         
20749
20750         if (this.isLocked()) {
20751             //Roo.log('locked');
20752             return;
20753         }
20754
20755         this.DDM.refreshCache(this.groups);
20756 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20757         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20758         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20759             //Roo.log('no outer handes or not over target');
20760                 // do nothing.
20761         } else {
20762 //            Roo.log('check validator');
20763             if (this.clickValidator(e)) {
20764 //                Roo.log('validate success');
20765                 // set the initial element position
20766                 this.setStartPosition();
20767
20768
20769                 this.b4MouseDown(e);
20770                 this.onMouseDown(e);
20771
20772                 this.DDM.handleMouseDown(e, this);
20773
20774                 this.DDM.stopEvent(e);
20775             } else {
20776
20777
20778             }
20779         }
20780     },
20781
20782     clickValidator: function(e) {
20783         var target = e.getTarget();
20784         return ( this.isValidHandleChild(target) &&
20785                     (this.id == this.handleElId ||
20786                         this.DDM.handleWasClicked(target, this.id)) );
20787     },
20788
20789     /**
20790      * Allows you to specify a tag name that should not start a drag operation
20791      * when clicked.  This is designed to facilitate embedding links within a
20792      * drag handle that do something other than start the drag.
20793      * @method addInvalidHandleType
20794      * @param {string} tagName the type of element to exclude
20795      */
20796     addInvalidHandleType: function(tagName) {
20797         var type = tagName.toUpperCase();
20798         this.invalidHandleTypes[type] = type;
20799     },
20800
20801     /**
20802      * Lets you to specify an element id for a child of a drag handle
20803      * that should not initiate a drag
20804      * @method addInvalidHandleId
20805      * @param {string} id the element id of the element you wish to ignore
20806      */
20807     addInvalidHandleId: function(id) {
20808         if (typeof id !== "string") {
20809             id = Roo.id(id);
20810         }
20811         this.invalidHandleIds[id] = id;
20812     },
20813
20814     /**
20815      * Lets you specify a css class of elements that will not initiate a drag
20816      * @method addInvalidHandleClass
20817      * @param {string} cssClass the class of the elements you wish to ignore
20818      */
20819     addInvalidHandleClass: function(cssClass) {
20820         this.invalidHandleClasses.push(cssClass);
20821     },
20822
20823     /**
20824      * Unsets an excluded tag name set by addInvalidHandleType
20825      * @method removeInvalidHandleType
20826      * @param {string} tagName the type of element to unexclude
20827      */
20828     removeInvalidHandleType: function(tagName) {
20829         var type = tagName.toUpperCase();
20830         // this.invalidHandleTypes[type] = null;
20831         delete this.invalidHandleTypes[type];
20832     },
20833
20834     /**
20835      * Unsets an invalid handle id
20836      * @method removeInvalidHandleId
20837      * @param {string} id the id of the element to re-enable
20838      */
20839     removeInvalidHandleId: function(id) {
20840         if (typeof id !== "string") {
20841             id = Roo.id(id);
20842         }
20843         delete this.invalidHandleIds[id];
20844     },
20845
20846     /**
20847      * Unsets an invalid css class
20848      * @method removeInvalidHandleClass
20849      * @param {string} cssClass the class of the element(s) you wish to
20850      * re-enable
20851      */
20852     removeInvalidHandleClass: function(cssClass) {
20853         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20854             if (this.invalidHandleClasses[i] == cssClass) {
20855                 delete this.invalidHandleClasses[i];
20856             }
20857         }
20858     },
20859
20860     /**
20861      * Checks the tag exclusion list to see if this click should be ignored
20862      * @method isValidHandleChild
20863      * @param {HTMLElement} node the HTMLElement to evaluate
20864      * @return {boolean} true if this is a valid tag type, false if not
20865      */
20866     isValidHandleChild: function(node) {
20867
20868         var valid = true;
20869         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20870         var nodeName;
20871         try {
20872             nodeName = node.nodeName.toUpperCase();
20873         } catch(e) {
20874             nodeName = node.nodeName;
20875         }
20876         valid = valid && !this.invalidHandleTypes[nodeName];
20877         valid = valid && !this.invalidHandleIds[node.id];
20878
20879         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20880             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20881         }
20882
20883
20884         return valid;
20885
20886     },
20887
20888     /**
20889      * Create the array of horizontal tick marks if an interval was specified
20890      * in setXConstraint().
20891      * @method setXTicks
20892      * @private
20893      */
20894     setXTicks: function(iStartX, iTickSize) {
20895         this.xTicks = [];
20896         this.xTickSize = iTickSize;
20897
20898         var tickMap = {};
20899
20900         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20901             if (!tickMap[i]) {
20902                 this.xTicks[this.xTicks.length] = i;
20903                 tickMap[i] = true;
20904             }
20905         }
20906
20907         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20908             if (!tickMap[i]) {
20909                 this.xTicks[this.xTicks.length] = i;
20910                 tickMap[i] = true;
20911             }
20912         }
20913
20914         this.xTicks.sort(this.DDM.numericSort) ;
20915     },
20916
20917     /**
20918      * Create the array of vertical tick marks if an interval was specified in
20919      * setYConstraint().
20920      * @method setYTicks
20921      * @private
20922      */
20923     setYTicks: function(iStartY, iTickSize) {
20924         this.yTicks = [];
20925         this.yTickSize = iTickSize;
20926
20927         var tickMap = {};
20928
20929         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20930             if (!tickMap[i]) {
20931                 this.yTicks[this.yTicks.length] = i;
20932                 tickMap[i] = true;
20933             }
20934         }
20935
20936         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20937             if (!tickMap[i]) {
20938                 this.yTicks[this.yTicks.length] = i;
20939                 tickMap[i] = true;
20940             }
20941         }
20942
20943         this.yTicks.sort(this.DDM.numericSort) ;
20944     },
20945
20946     /**
20947      * By default, the element can be dragged any place on the screen.  Use
20948      * this method to limit the horizontal travel of the element.  Pass in
20949      * 0,0 for the parameters if you want to lock the drag to the y axis.
20950      * @method setXConstraint
20951      * @param {int} iLeft the number of pixels the element can move to the left
20952      * @param {int} iRight the number of pixels the element can move to the
20953      * right
20954      * @param {int} iTickSize optional parameter for specifying that the
20955      * element
20956      * should move iTickSize pixels at a time.
20957      */
20958     setXConstraint: function(iLeft, iRight, iTickSize) {
20959         this.leftConstraint = iLeft;
20960         this.rightConstraint = iRight;
20961
20962         this.minX = this.initPageX - iLeft;
20963         this.maxX = this.initPageX + iRight;
20964         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20965
20966         this.constrainX = true;
20967     },
20968
20969     /**
20970      * Clears any constraints applied to this instance.  Also clears ticks
20971      * since they can't exist independent of a constraint at this time.
20972      * @method clearConstraints
20973      */
20974     clearConstraints: function() {
20975         this.constrainX = false;
20976         this.constrainY = false;
20977         this.clearTicks();
20978     },
20979
20980     /**
20981      * Clears any tick interval defined for this instance
20982      * @method clearTicks
20983      */
20984     clearTicks: function() {
20985         this.xTicks = null;
20986         this.yTicks = null;
20987         this.xTickSize = 0;
20988         this.yTickSize = 0;
20989     },
20990
20991     /**
20992      * By default, the element can be dragged any place on the screen.  Set
20993      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20994      * parameters if you want to lock the drag to the x axis.
20995      * @method setYConstraint
20996      * @param {int} iUp the number of pixels the element can move up
20997      * @param {int} iDown the number of pixels the element can move down
20998      * @param {int} iTickSize optional parameter for specifying that the
20999      * element should move iTickSize pixels at a time.
21000      */
21001     setYConstraint: function(iUp, iDown, iTickSize) {
21002         this.topConstraint = iUp;
21003         this.bottomConstraint = iDown;
21004
21005         this.minY = this.initPageY - iUp;
21006         this.maxY = this.initPageY + iDown;
21007         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21008
21009         this.constrainY = true;
21010
21011     },
21012
21013     /**
21014      * resetConstraints must be called if you manually reposition a dd element.
21015      * @method resetConstraints
21016      * @param {boolean} maintainOffset
21017      */
21018     resetConstraints: function() {
21019
21020
21021         // Maintain offsets if necessary
21022         if (this.initPageX || this.initPageX === 0) {
21023             // figure out how much this thing has moved
21024             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21025             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21026
21027             this.setInitPosition(dx, dy);
21028
21029         // This is the first time we have detected the element's position
21030         } else {
21031             this.setInitPosition();
21032         }
21033
21034         if (this.constrainX) {
21035             this.setXConstraint( this.leftConstraint,
21036                                  this.rightConstraint,
21037                                  this.xTickSize        );
21038         }
21039
21040         if (this.constrainY) {
21041             this.setYConstraint( this.topConstraint,
21042                                  this.bottomConstraint,
21043                                  this.yTickSize         );
21044         }
21045     },
21046
21047     /**
21048      * Normally the drag element is moved pixel by pixel, but we can specify
21049      * that it move a number of pixels at a time.  This method resolves the
21050      * location when we have it set up like this.
21051      * @method getTick
21052      * @param {int} val where we want to place the object
21053      * @param {int[]} tickArray sorted array of valid points
21054      * @return {int} the closest tick
21055      * @private
21056      */
21057     getTick: function(val, tickArray) {
21058
21059         if (!tickArray) {
21060             // If tick interval is not defined, it is effectively 1 pixel,
21061             // so we return the value passed to us.
21062             return val;
21063         } else if (tickArray[0] >= val) {
21064             // The value is lower than the first tick, so we return the first
21065             // tick.
21066             return tickArray[0];
21067         } else {
21068             for (var i=0, len=tickArray.length; i<len; ++i) {
21069                 var next = i + 1;
21070                 if (tickArray[next] && tickArray[next] >= val) {
21071                     var diff1 = val - tickArray[i];
21072                     var diff2 = tickArray[next] - val;
21073                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21074                 }
21075             }
21076
21077             // The value is larger than the last tick, so we return the last
21078             // tick.
21079             return tickArray[tickArray.length - 1];
21080         }
21081     },
21082
21083     /**
21084      * toString method
21085      * @method toString
21086      * @return {string} string representation of the dd obj
21087      */
21088     toString: function() {
21089         return ("DragDrop " + this.id);
21090     }
21091
21092 });
21093
21094 })();
21095 /*
21096  * Based on:
21097  * Ext JS Library 1.1.1
21098  * Copyright(c) 2006-2007, Ext JS, LLC.
21099  *
21100  * Originally Released Under LGPL - original licence link has changed is not relivant.
21101  *
21102  * Fork - LGPL
21103  * <script type="text/javascript">
21104  */
21105
21106
21107 /**
21108  * The drag and drop utility provides a framework for building drag and drop
21109  * applications.  In addition to enabling drag and drop for specific elements,
21110  * the drag and drop elements are tracked by the manager class, and the
21111  * interactions between the various elements are tracked during the drag and
21112  * the implementing code is notified about these important moments.
21113  */
21114
21115 // Only load the library once.  Rewriting the manager class would orphan
21116 // existing drag and drop instances.
21117 if (!Roo.dd.DragDropMgr) {
21118
21119 /**
21120  * @class Roo.dd.DragDropMgr
21121  * DragDropMgr is a singleton that tracks the element interaction for
21122  * all DragDrop items in the window.  Generally, you will not call
21123  * this class directly, but it does have helper methods that could
21124  * be useful in your DragDrop implementations.
21125  * @static
21126  */
21127 Roo.dd.DragDropMgr = function() {
21128
21129     var Event = Roo.EventManager;
21130
21131     return {
21132
21133         /**
21134          * Two dimensional Array of registered DragDrop objects.  The first
21135          * dimension is the DragDrop item group, the second the DragDrop
21136          * object.
21137          * @property ids
21138          * @type {string: string}
21139          * @private
21140          * @static
21141          */
21142         ids: {},
21143
21144         /**
21145          * Array of element ids defined as drag handles.  Used to determine
21146          * if the element that generated the mousedown event is actually the
21147          * handle and not the html element itself.
21148          * @property handleIds
21149          * @type {string: string}
21150          * @private
21151          * @static
21152          */
21153         handleIds: {},
21154
21155         /**
21156          * the DragDrop object that is currently being dragged
21157          * @property dragCurrent
21158          * @type DragDrop
21159          * @private
21160          * @static
21161          **/
21162         dragCurrent: null,
21163
21164         /**
21165          * the DragDrop object(s) that are being hovered over
21166          * @property dragOvers
21167          * @type Array
21168          * @private
21169          * @static
21170          */
21171         dragOvers: {},
21172
21173         /**
21174          * the X distance between the cursor and the object being dragged
21175          * @property deltaX
21176          * @type int
21177          * @private
21178          * @static
21179          */
21180         deltaX: 0,
21181
21182         /**
21183          * the Y distance between the cursor and the object being dragged
21184          * @property deltaY
21185          * @type int
21186          * @private
21187          * @static
21188          */
21189         deltaY: 0,
21190
21191         /**
21192          * Flag to determine if we should prevent the default behavior of the
21193          * events we define. By default this is true, but this can be set to
21194          * false if you need the default behavior (not recommended)
21195          * @property preventDefault
21196          * @type boolean
21197          * @static
21198          */
21199         preventDefault: true,
21200
21201         /**
21202          * Flag to determine if we should stop the propagation of the events
21203          * we generate. This is true by default but you may want to set it to
21204          * false if the html element contains other features that require the
21205          * mouse click.
21206          * @property stopPropagation
21207          * @type boolean
21208          * @static
21209          */
21210         stopPropagation: true,
21211
21212         /**
21213          * Internal flag that is set to true when drag and drop has been
21214          * intialized
21215          * @property initialized
21216          * @private
21217          * @static
21218          */
21219         initalized: false,
21220
21221         /**
21222          * All drag and drop can be disabled.
21223          * @property locked
21224          * @private
21225          * @static
21226          */
21227         locked: false,
21228
21229         /**
21230          * Called the first time an element is registered.
21231          * @method init
21232          * @private
21233          * @static
21234          */
21235         init: function() {
21236             this.initialized = true;
21237         },
21238
21239         /**
21240          * In point mode, drag and drop interaction is defined by the
21241          * location of the cursor during the drag/drop
21242          * @property POINT
21243          * @type int
21244          * @static
21245          */
21246         POINT: 0,
21247
21248         /**
21249          * In intersect mode, drag and drop interactio nis defined by the
21250          * overlap of two or more drag and drop objects.
21251          * @property INTERSECT
21252          * @type int
21253          * @static
21254          */
21255         INTERSECT: 1,
21256
21257         /**
21258          * The current drag and drop mode.  Default: POINT
21259          * @property mode
21260          * @type int
21261          * @static
21262          */
21263         mode: 0,
21264
21265         /**
21266          * Runs method on all drag and drop objects
21267          * @method _execOnAll
21268          * @private
21269          * @static
21270          */
21271         _execOnAll: function(sMethod, args) {
21272             for (var i in this.ids) {
21273                 for (var j in this.ids[i]) {
21274                     var oDD = this.ids[i][j];
21275                     if (! this.isTypeOfDD(oDD)) {
21276                         continue;
21277                     }
21278                     oDD[sMethod].apply(oDD, args);
21279                 }
21280             }
21281         },
21282
21283         /**
21284          * Drag and drop initialization.  Sets up the global event handlers
21285          * @method _onLoad
21286          * @private
21287          * @static
21288          */
21289         _onLoad: function() {
21290
21291             this.init();
21292
21293             if (!Roo.isTouch) {
21294                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21295                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21296             }
21297             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21298             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21299             
21300             Event.on(window,   "unload",    this._onUnload, this, true);
21301             Event.on(window,   "resize",    this._onResize, this, true);
21302             // Event.on(window,   "mouseout",    this._test);
21303
21304         },
21305
21306         /**
21307          * Reset constraints on all drag and drop objs
21308          * @method _onResize
21309          * @private
21310          * @static
21311          */
21312         _onResize: function(e) {
21313             this._execOnAll("resetConstraints", []);
21314         },
21315
21316         /**
21317          * Lock all drag and drop functionality
21318          * @method lock
21319          * @static
21320          */
21321         lock: function() { this.locked = true; },
21322
21323         /**
21324          * Unlock all drag and drop functionality
21325          * @method unlock
21326          * @static
21327          */
21328         unlock: function() { this.locked = false; },
21329
21330         /**
21331          * Is drag and drop locked?
21332          * @method isLocked
21333          * @return {boolean} True if drag and drop is locked, false otherwise.
21334          * @static
21335          */
21336         isLocked: function() { return this.locked; },
21337
21338         /**
21339          * Location cache that is set for all drag drop objects when a drag is
21340          * initiated, cleared when the drag is finished.
21341          * @property locationCache
21342          * @private
21343          * @static
21344          */
21345         locationCache: {},
21346
21347         /**
21348          * Set useCache to false if you want to force object the lookup of each
21349          * drag and drop linked element constantly during a drag.
21350          * @property useCache
21351          * @type boolean
21352          * @static
21353          */
21354         useCache: true,
21355
21356         /**
21357          * The number of pixels that the mouse needs to move after the
21358          * mousedown before the drag is initiated.  Default=3;
21359          * @property clickPixelThresh
21360          * @type int
21361          * @static
21362          */
21363         clickPixelThresh: 3,
21364
21365         /**
21366          * The number of milliseconds after the mousedown event to initiate the
21367          * drag if we don't get a mouseup event. Default=1000
21368          * @property clickTimeThresh
21369          * @type int
21370          * @static
21371          */
21372         clickTimeThresh: 350,
21373
21374         /**
21375          * Flag that indicates that either the drag pixel threshold or the
21376          * mousdown time threshold has been met
21377          * @property dragThreshMet
21378          * @type boolean
21379          * @private
21380          * @static
21381          */
21382         dragThreshMet: false,
21383
21384         /**
21385          * Timeout used for the click time threshold
21386          * @property clickTimeout
21387          * @type Object
21388          * @private
21389          * @static
21390          */
21391         clickTimeout: null,
21392
21393         /**
21394          * The X position of the mousedown event stored for later use when a
21395          * drag threshold is met.
21396          * @property startX
21397          * @type int
21398          * @private
21399          * @static
21400          */
21401         startX: 0,
21402
21403         /**
21404          * The Y position of the mousedown event stored for later use when a
21405          * drag threshold is met.
21406          * @property startY
21407          * @type int
21408          * @private
21409          * @static
21410          */
21411         startY: 0,
21412
21413         /**
21414          * Each DragDrop instance must be registered with the DragDropMgr.
21415          * This is executed in DragDrop.init()
21416          * @method regDragDrop
21417          * @param {DragDrop} oDD the DragDrop object to register
21418          * @param {String} sGroup the name of the group this element belongs to
21419          * @static
21420          */
21421         regDragDrop: function(oDD, sGroup) {
21422             if (!this.initialized) { this.init(); }
21423
21424             if (!this.ids[sGroup]) {
21425                 this.ids[sGroup] = {};
21426             }
21427             this.ids[sGroup][oDD.id] = oDD;
21428         },
21429
21430         /**
21431          * Removes the supplied dd instance from the supplied group. Executed
21432          * by DragDrop.removeFromGroup, so don't call this function directly.
21433          * @method removeDDFromGroup
21434          * @private
21435          * @static
21436          */
21437         removeDDFromGroup: function(oDD, sGroup) {
21438             if (!this.ids[sGroup]) {
21439                 this.ids[sGroup] = {};
21440             }
21441
21442             var obj = this.ids[sGroup];
21443             if (obj && obj[oDD.id]) {
21444                 delete obj[oDD.id];
21445             }
21446         },
21447
21448         /**
21449          * Unregisters a drag and drop item.  This is executed in
21450          * DragDrop.unreg, use that method instead of calling this directly.
21451          * @method _remove
21452          * @private
21453          * @static
21454          */
21455         _remove: function(oDD) {
21456             for (var g in oDD.groups) {
21457                 if (g && this.ids[g][oDD.id]) {
21458                     delete this.ids[g][oDD.id];
21459                 }
21460             }
21461             delete this.handleIds[oDD.id];
21462         },
21463
21464         /**
21465          * Each DragDrop handle element must be registered.  This is done
21466          * automatically when executing DragDrop.setHandleElId()
21467          * @method regHandle
21468          * @param {String} sDDId the DragDrop id this element is a handle for
21469          * @param {String} sHandleId the id of the element that is the drag
21470          * handle
21471          * @static
21472          */
21473         regHandle: function(sDDId, sHandleId) {
21474             if (!this.handleIds[sDDId]) {
21475                 this.handleIds[sDDId] = {};
21476             }
21477             this.handleIds[sDDId][sHandleId] = sHandleId;
21478         },
21479
21480         /**
21481          * Utility function to determine if a given element has been
21482          * registered as a drag drop item.
21483          * @method isDragDrop
21484          * @param {String} id the element id to check
21485          * @return {boolean} true if this element is a DragDrop item,
21486          * false otherwise
21487          * @static
21488          */
21489         isDragDrop: function(id) {
21490             return ( this.getDDById(id) ) ? true : false;
21491         },
21492
21493         /**
21494          * Returns the drag and drop instances that are in all groups the
21495          * passed in instance belongs to.
21496          * @method getRelated
21497          * @param {DragDrop} p_oDD the obj to get related data for
21498          * @param {boolean} bTargetsOnly if true, only return targetable objs
21499          * @return {DragDrop[]} the related instances
21500          * @static
21501          */
21502         getRelated: function(p_oDD, bTargetsOnly) {
21503             var oDDs = [];
21504             for (var i in p_oDD.groups) {
21505                 for (j in this.ids[i]) {
21506                     var dd = this.ids[i][j];
21507                     if (! this.isTypeOfDD(dd)) {
21508                         continue;
21509                     }
21510                     if (!bTargetsOnly || dd.isTarget) {
21511                         oDDs[oDDs.length] = dd;
21512                     }
21513                 }
21514             }
21515
21516             return oDDs;
21517         },
21518
21519         /**
21520          * Returns true if the specified dd target is a legal target for
21521          * the specifice drag obj
21522          * @method isLegalTarget
21523          * @param {DragDrop} the drag obj
21524          * @param {DragDrop} the target
21525          * @return {boolean} true if the target is a legal target for the
21526          * dd obj
21527          * @static
21528          */
21529         isLegalTarget: function (oDD, oTargetDD) {
21530             var targets = this.getRelated(oDD, true);
21531             for (var i=0, len=targets.length;i<len;++i) {
21532                 if (targets[i].id == oTargetDD.id) {
21533                     return true;
21534                 }
21535             }
21536
21537             return false;
21538         },
21539
21540         /**
21541          * My goal is to be able to transparently determine if an object is
21542          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21543          * returns "object", oDD.constructor.toString() always returns
21544          * "DragDrop" and not the name of the subclass.  So for now it just
21545          * evaluates a well-known variable in DragDrop.
21546          * @method isTypeOfDD
21547          * @param {Object} the object to evaluate
21548          * @return {boolean} true if typeof oDD = DragDrop
21549          * @static
21550          */
21551         isTypeOfDD: function (oDD) {
21552             return (oDD && oDD.__ygDragDrop);
21553         },
21554
21555         /**
21556          * Utility function to determine if a given element has been
21557          * registered as a drag drop handle for the given Drag Drop object.
21558          * @method isHandle
21559          * @param {String} id the element id to check
21560          * @return {boolean} true if this element is a DragDrop handle, false
21561          * otherwise
21562          * @static
21563          */
21564         isHandle: function(sDDId, sHandleId) {
21565             return ( this.handleIds[sDDId] &&
21566                             this.handleIds[sDDId][sHandleId] );
21567         },
21568
21569         /**
21570          * Returns the DragDrop instance for a given id
21571          * @method getDDById
21572          * @param {String} id the id of the DragDrop object
21573          * @return {DragDrop} the drag drop object, null if it is not found
21574          * @static
21575          */
21576         getDDById: function(id) {
21577             for (var i in this.ids) {
21578                 if (this.ids[i][id]) {
21579                     return this.ids[i][id];
21580                 }
21581             }
21582             return null;
21583         },
21584
21585         /**
21586          * Fired after a registered DragDrop object gets the mousedown event.
21587          * Sets up the events required to track the object being dragged
21588          * @method handleMouseDown
21589          * @param {Event} e the event
21590          * @param oDD the DragDrop object being dragged
21591          * @private
21592          * @static
21593          */
21594         handleMouseDown: function(e, oDD) {
21595             if(Roo.QuickTips){
21596                 Roo.QuickTips.disable();
21597             }
21598             this.currentTarget = e.getTarget();
21599
21600             this.dragCurrent = oDD;
21601
21602             var el = oDD.getEl();
21603
21604             // track start position
21605             this.startX = e.getPageX();
21606             this.startY = e.getPageY();
21607
21608             this.deltaX = this.startX - el.offsetLeft;
21609             this.deltaY = this.startY - el.offsetTop;
21610
21611             this.dragThreshMet = false;
21612
21613             this.clickTimeout = setTimeout(
21614                     function() {
21615                         var DDM = Roo.dd.DDM;
21616                         DDM.startDrag(DDM.startX, DDM.startY);
21617                     },
21618                     this.clickTimeThresh );
21619         },
21620
21621         /**
21622          * Fired when either the drag pixel threshol or the mousedown hold
21623          * time threshold has been met.
21624          * @method startDrag
21625          * @param x {int} the X position of the original mousedown
21626          * @param y {int} the Y position of the original mousedown
21627          * @static
21628          */
21629         startDrag: function(x, y) {
21630             clearTimeout(this.clickTimeout);
21631             if (this.dragCurrent) {
21632                 this.dragCurrent.b4StartDrag(x, y);
21633                 this.dragCurrent.startDrag(x, y);
21634             }
21635             this.dragThreshMet = true;
21636         },
21637
21638         /**
21639          * Internal function to handle the mouseup event.  Will be invoked
21640          * from the context of the document.
21641          * @method handleMouseUp
21642          * @param {Event} e the event
21643          * @private
21644          * @static
21645          */
21646         handleMouseUp: function(e) {
21647
21648             if(Roo.QuickTips){
21649                 Roo.QuickTips.enable();
21650             }
21651             if (! this.dragCurrent) {
21652                 return;
21653             }
21654
21655             clearTimeout(this.clickTimeout);
21656
21657             if (this.dragThreshMet) {
21658                 this.fireEvents(e, true);
21659             } else {
21660             }
21661
21662             this.stopDrag(e);
21663
21664             this.stopEvent(e);
21665         },
21666
21667         /**
21668          * Utility to stop event propagation and event default, if these
21669          * features are turned on.
21670          * @method stopEvent
21671          * @param {Event} e the event as returned by this.getEvent()
21672          * @static
21673          */
21674         stopEvent: function(e){
21675             if(this.stopPropagation) {
21676                 e.stopPropagation();
21677             }
21678
21679             if (this.preventDefault) {
21680                 e.preventDefault();
21681             }
21682         },
21683
21684         /**
21685          * Internal function to clean up event handlers after the drag
21686          * operation is complete
21687          * @method stopDrag
21688          * @param {Event} e the event
21689          * @private
21690          * @static
21691          */
21692         stopDrag: function(e) {
21693             // Fire the drag end event for the item that was dragged
21694             if (this.dragCurrent) {
21695                 if (this.dragThreshMet) {
21696                     this.dragCurrent.b4EndDrag(e);
21697                     this.dragCurrent.endDrag(e);
21698                 }
21699
21700                 this.dragCurrent.onMouseUp(e);
21701             }
21702
21703             this.dragCurrent = null;
21704             this.dragOvers = {};
21705         },
21706
21707         /**
21708          * Internal function to handle the mousemove event.  Will be invoked
21709          * from the context of the html element.
21710          *
21711          * @TODO figure out what we can do about mouse events lost when the
21712          * user drags objects beyond the window boundary.  Currently we can
21713          * detect this in internet explorer by verifying that the mouse is
21714          * down during the mousemove event.  Firefox doesn't give us the
21715          * button state on the mousemove event.
21716          * @method handleMouseMove
21717          * @param {Event} e the event
21718          * @private
21719          * @static
21720          */
21721         handleMouseMove: function(e) {
21722             if (! this.dragCurrent) {
21723                 return true;
21724             }
21725
21726             // var button = e.which || e.button;
21727
21728             // check for IE mouseup outside of page boundary
21729             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21730                 this.stopEvent(e);
21731                 return this.handleMouseUp(e);
21732             }
21733
21734             if (!this.dragThreshMet) {
21735                 var diffX = Math.abs(this.startX - e.getPageX());
21736                 var diffY = Math.abs(this.startY - e.getPageY());
21737                 if (diffX > this.clickPixelThresh ||
21738                             diffY > this.clickPixelThresh) {
21739                     this.startDrag(this.startX, this.startY);
21740                 }
21741             }
21742
21743             if (this.dragThreshMet) {
21744                 this.dragCurrent.b4Drag(e);
21745                 this.dragCurrent.onDrag(e);
21746                 if(!this.dragCurrent.moveOnly){
21747                     this.fireEvents(e, false);
21748                 }
21749             }
21750
21751             this.stopEvent(e);
21752
21753             return true;
21754         },
21755
21756         /**
21757          * Iterates over all of the DragDrop elements to find ones we are
21758          * hovering over or dropping on
21759          * @method fireEvents
21760          * @param {Event} e the event
21761          * @param {boolean} isDrop is this a drop op or a mouseover op?
21762          * @private
21763          * @static
21764          */
21765         fireEvents: function(e, isDrop) {
21766             var dc = this.dragCurrent;
21767
21768             // If the user did the mouse up outside of the window, we could
21769             // get here even though we have ended the drag.
21770             if (!dc || dc.isLocked()) {
21771                 return;
21772             }
21773
21774             var pt = e.getPoint();
21775
21776             // cache the previous dragOver array
21777             var oldOvers = [];
21778
21779             var outEvts   = [];
21780             var overEvts  = [];
21781             var dropEvts  = [];
21782             var enterEvts = [];
21783
21784             // Check to see if the object(s) we were hovering over is no longer
21785             // being hovered over so we can fire the onDragOut event
21786             for (var i in this.dragOvers) {
21787
21788                 var ddo = this.dragOvers[i];
21789
21790                 if (! this.isTypeOfDD(ddo)) {
21791                     continue;
21792                 }
21793
21794                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21795                     outEvts.push( ddo );
21796                 }
21797
21798                 oldOvers[i] = true;
21799                 delete this.dragOvers[i];
21800             }
21801
21802             for (var sGroup in dc.groups) {
21803
21804                 if ("string" != typeof sGroup) {
21805                     continue;
21806                 }
21807
21808                 for (i in this.ids[sGroup]) {
21809                     var oDD = this.ids[sGroup][i];
21810                     if (! this.isTypeOfDD(oDD)) {
21811                         continue;
21812                     }
21813
21814                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21815                         if (this.isOverTarget(pt, oDD, this.mode)) {
21816                             // look for drop interactions
21817                             if (isDrop) {
21818                                 dropEvts.push( oDD );
21819                             // look for drag enter and drag over interactions
21820                             } else {
21821
21822                                 // initial drag over: dragEnter fires
21823                                 if (!oldOvers[oDD.id]) {
21824                                     enterEvts.push( oDD );
21825                                 // subsequent drag overs: dragOver fires
21826                                 } else {
21827                                     overEvts.push( oDD );
21828                                 }
21829
21830                                 this.dragOvers[oDD.id] = oDD;
21831                             }
21832                         }
21833                     }
21834                 }
21835             }
21836
21837             if (this.mode) {
21838                 if (outEvts.length) {
21839                     dc.b4DragOut(e, outEvts);
21840                     dc.onDragOut(e, outEvts);
21841                 }
21842
21843                 if (enterEvts.length) {
21844                     dc.onDragEnter(e, enterEvts);
21845                 }
21846
21847                 if (overEvts.length) {
21848                     dc.b4DragOver(e, overEvts);
21849                     dc.onDragOver(e, overEvts);
21850                 }
21851
21852                 if (dropEvts.length) {
21853                     dc.b4DragDrop(e, dropEvts);
21854                     dc.onDragDrop(e, dropEvts);
21855                 }
21856
21857             } else {
21858                 // fire dragout events
21859                 var len = 0;
21860                 for (i=0, len=outEvts.length; i<len; ++i) {
21861                     dc.b4DragOut(e, outEvts[i].id);
21862                     dc.onDragOut(e, outEvts[i].id);
21863                 }
21864
21865                 // fire enter events
21866                 for (i=0,len=enterEvts.length; i<len; ++i) {
21867                     // dc.b4DragEnter(e, oDD.id);
21868                     dc.onDragEnter(e, enterEvts[i].id);
21869                 }
21870
21871                 // fire over events
21872                 for (i=0,len=overEvts.length; i<len; ++i) {
21873                     dc.b4DragOver(e, overEvts[i].id);
21874                     dc.onDragOver(e, overEvts[i].id);
21875                 }
21876
21877                 // fire drop events
21878                 for (i=0, len=dropEvts.length; i<len; ++i) {
21879                     dc.b4DragDrop(e, dropEvts[i].id);
21880                     dc.onDragDrop(e, dropEvts[i].id);
21881                 }
21882
21883             }
21884
21885             // notify about a drop that did not find a target
21886             if (isDrop && !dropEvts.length) {
21887                 dc.onInvalidDrop(e);
21888             }
21889
21890         },
21891
21892         /**
21893          * Helper function for getting the best match from the list of drag
21894          * and drop objects returned by the drag and drop events when we are
21895          * in INTERSECT mode.  It returns either the first object that the
21896          * cursor is over, or the object that has the greatest overlap with
21897          * the dragged element.
21898          * @method getBestMatch
21899          * @param  {DragDrop[]} dds The array of drag and drop objects
21900          * targeted
21901          * @return {DragDrop}       The best single match
21902          * @static
21903          */
21904         getBestMatch: function(dds) {
21905             var winner = null;
21906             // Return null if the input is not what we expect
21907             //if (!dds || !dds.length || dds.length == 0) {
21908                // winner = null;
21909             // If there is only one item, it wins
21910             //} else if (dds.length == 1) {
21911
21912             var len = dds.length;
21913
21914             if (len == 1) {
21915                 winner = dds[0];
21916             } else {
21917                 // Loop through the targeted items
21918                 for (var i=0; i<len; ++i) {
21919                     var dd = dds[i];
21920                     // If the cursor is over the object, it wins.  If the
21921                     // cursor is over multiple matches, the first one we come
21922                     // to wins.
21923                     if (dd.cursorIsOver) {
21924                         winner = dd;
21925                         break;
21926                     // Otherwise the object with the most overlap wins
21927                     } else {
21928                         if (!winner ||
21929                             winner.overlap.getArea() < dd.overlap.getArea()) {
21930                             winner = dd;
21931                         }
21932                     }
21933                 }
21934             }
21935
21936             return winner;
21937         },
21938
21939         /**
21940          * Refreshes the cache of the top-left and bottom-right points of the
21941          * drag and drop objects in the specified group(s).  This is in the
21942          * format that is stored in the drag and drop instance, so typical
21943          * usage is:
21944          * <code>
21945          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21946          * </code>
21947          * Alternatively:
21948          * <code>
21949          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21950          * </code>
21951          * @TODO this really should be an indexed array.  Alternatively this
21952          * method could accept both.
21953          * @method refreshCache
21954          * @param {Object} groups an associative array of groups to refresh
21955          * @static
21956          */
21957         refreshCache: function(groups) {
21958             for (var sGroup in groups) {
21959                 if ("string" != typeof sGroup) {
21960                     continue;
21961                 }
21962                 for (var i in this.ids[sGroup]) {
21963                     var oDD = this.ids[sGroup][i];
21964
21965                     if (this.isTypeOfDD(oDD)) {
21966                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21967                         var loc = this.getLocation(oDD);
21968                         if (loc) {
21969                             this.locationCache[oDD.id] = loc;
21970                         } else {
21971                             delete this.locationCache[oDD.id];
21972                             // this will unregister the drag and drop object if
21973                             // the element is not in a usable state
21974                             // oDD.unreg();
21975                         }
21976                     }
21977                 }
21978             }
21979         },
21980
21981         /**
21982          * This checks to make sure an element exists and is in the DOM.  The
21983          * main purpose is to handle cases where innerHTML is used to remove
21984          * drag and drop objects from the DOM.  IE provides an 'unspecified
21985          * error' when trying to access the offsetParent of such an element
21986          * @method verifyEl
21987          * @param {HTMLElement} el the element to check
21988          * @return {boolean} true if the element looks usable
21989          * @static
21990          */
21991         verifyEl: function(el) {
21992             if (el) {
21993                 var parent;
21994                 if(Roo.isIE){
21995                     try{
21996                         parent = el.offsetParent;
21997                     }catch(e){}
21998                 }else{
21999                     parent = el.offsetParent;
22000                 }
22001                 if (parent) {
22002                     return true;
22003                 }
22004             }
22005
22006             return false;
22007         },
22008
22009         /**
22010          * Returns a Region object containing the drag and drop element's position
22011          * and size, including the padding configured for it
22012          * @method getLocation
22013          * @param {DragDrop} oDD the drag and drop object to get the
22014          *                       location for
22015          * @return {Roo.lib.Region} a Region object representing the total area
22016          *                             the element occupies, including any padding
22017          *                             the instance is configured for.
22018          * @static
22019          */
22020         getLocation: function(oDD) {
22021             if (! this.isTypeOfDD(oDD)) {
22022                 return null;
22023             }
22024
22025             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22026
22027             try {
22028                 pos= Roo.lib.Dom.getXY(el);
22029             } catch (e) { }
22030
22031             if (!pos) {
22032                 return null;
22033             }
22034
22035             x1 = pos[0];
22036             x2 = x1 + el.offsetWidth;
22037             y1 = pos[1];
22038             y2 = y1 + el.offsetHeight;
22039
22040             t = y1 - oDD.padding[0];
22041             r = x2 + oDD.padding[1];
22042             b = y2 + oDD.padding[2];
22043             l = x1 - oDD.padding[3];
22044
22045             return new Roo.lib.Region( t, r, b, l );
22046         },
22047
22048         /**
22049          * Checks the cursor location to see if it over the target
22050          * @method isOverTarget
22051          * @param {Roo.lib.Point} pt The point to evaluate
22052          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22053          * @return {boolean} true if the mouse is over the target
22054          * @private
22055          * @static
22056          */
22057         isOverTarget: function(pt, oTarget, intersect) {
22058             // use cache if available
22059             var loc = this.locationCache[oTarget.id];
22060             if (!loc || !this.useCache) {
22061                 loc = this.getLocation(oTarget);
22062                 this.locationCache[oTarget.id] = loc;
22063
22064             }
22065
22066             if (!loc) {
22067                 return false;
22068             }
22069
22070             oTarget.cursorIsOver = loc.contains( pt );
22071
22072             // DragDrop is using this as a sanity check for the initial mousedown
22073             // in this case we are done.  In POINT mode, if the drag obj has no
22074             // contraints, we are also done. Otherwise we need to evaluate the
22075             // location of the target as related to the actual location of the
22076             // dragged element.
22077             var dc = this.dragCurrent;
22078             if (!dc || !dc.getTargetCoord ||
22079                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22080                 return oTarget.cursorIsOver;
22081             }
22082
22083             oTarget.overlap = null;
22084
22085             // Get the current location of the drag element, this is the
22086             // location of the mouse event less the delta that represents
22087             // where the original mousedown happened on the element.  We
22088             // need to consider constraints and ticks as well.
22089             var pos = dc.getTargetCoord(pt.x, pt.y);
22090
22091             var el = dc.getDragEl();
22092             var curRegion = new Roo.lib.Region( pos.y,
22093                                                    pos.x + el.offsetWidth,
22094                                                    pos.y + el.offsetHeight,
22095                                                    pos.x );
22096
22097             var overlap = curRegion.intersect(loc);
22098
22099             if (overlap) {
22100                 oTarget.overlap = overlap;
22101                 return (intersect) ? true : oTarget.cursorIsOver;
22102             } else {
22103                 return false;
22104             }
22105         },
22106
22107         /**
22108          * unload event handler
22109          * @method _onUnload
22110          * @private
22111          * @static
22112          */
22113         _onUnload: function(e, me) {
22114             Roo.dd.DragDropMgr.unregAll();
22115         },
22116
22117         /**
22118          * Cleans up the drag and drop events and objects.
22119          * @method unregAll
22120          * @private
22121          * @static
22122          */
22123         unregAll: function() {
22124
22125             if (this.dragCurrent) {
22126                 this.stopDrag();
22127                 this.dragCurrent = null;
22128             }
22129
22130             this._execOnAll("unreg", []);
22131
22132             for (i in this.elementCache) {
22133                 delete this.elementCache[i];
22134             }
22135
22136             this.elementCache = {};
22137             this.ids = {};
22138         },
22139
22140         /**
22141          * A cache of DOM elements
22142          * @property elementCache
22143          * @private
22144          * @static
22145          */
22146         elementCache: {},
22147
22148         /**
22149          * Get the wrapper for the DOM element specified
22150          * @method getElWrapper
22151          * @param {String} id the id of the element to get
22152          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22153          * @private
22154          * @deprecated This wrapper isn't that useful
22155          * @static
22156          */
22157         getElWrapper: function(id) {
22158             var oWrapper = this.elementCache[id];
22159             if (!oWrapper || !oWrapper.el) {
22160                 oWrapper = this.elementCache[id] =
22161                     new this.ElementWrapper(Roo.getDom(id));
22162             }
22163             return oWrapper;
22164         },
22165
22166         /**
22167          * Returns the actual DOM element
22168          * @method getElement
22169          * @param {String} id the id of the elment to get
22170          * @return {Object} The element
22171          * @deprecated use Roo.getDom instead
22172          * @static
22173          */
22174         getElement: function(id) {
22175             return Roo.getDom(id);
22176         },
22177
22178         /**
22179          * Returns the style property for the DOM element (i.e.,
22180          * document.getElById(id).style)
22181          * @method getCss
22182          * @param {String} id the id of the elment to get
22183          * @return {Object} The style property of the element
22184          * @deprecated use Roo.getDom instead
22185          * @static
22186          */
22187         getCss: function(id) {
22188             var el = Roo.getDom(id);
22189             return (el) ? el.style : null;
22190         },
22191
22192         /**
22193          * Inner class for cached elements
22194          * @class DragDropMgr.ElementWrapper
22195          * @for DragDropMgr
22196          * @private
22197          * @deprecated
22198          */
22199         ElementWrapper: function(el) {
22200                 /**
22201                  * The element
22202                  * @property el
22203                  */
22204                 this.el = el || null;
22205                 /**
22206                  * The element id
22207                  * @property id
22208                  */
22209                 this.id = this.el && el.id;
22210                 /**
22211                  * A reference to the style property
22212                  * @property css
22213                  */
22214                 this.css = this.el && el.style;
22215             },
22216
22217         /**
22218          * Returns the X position of an html element
22219          * @method getPosX
22220          * @param el the element for which to get the position
22221          * @return {int} the X coordinate
22222          * @for DragDropMgr
22223          * @deprecated use Roo.lib.Dom.getX instead
22224          * @static
22225          */
22226         getPosX: function(el) {
22227             return Roo.lib.Dom.getX(el);
22228         },
22229
22230         /**
22231          * Returns the Y position of an html element
22232          * @method getPosY
22233          * @param el the element for which to get the position
22234          * @return {int} the Y coordinate
22235          * @deprecated use Roo.lib.Dom.getY instead
22236          * @static
22237          */
22238         getPosY: function(el) {
22239             return Roo.lib.Dom.getY(el);
22240         },
22241
22242         /**
22243          * Swap two nodes.  In IE, we use the native method, for others we
22244          * emulate the IE behavior
22245          * @method swapNode
22246          * @param n1 the first node to swap
22247          * @param n2 the other node to swap
22248          * @static
22249          */
22250         swapNode: function(n1, n2) {
22251             if (n1.swapNode) {
22252                 n1.swapNode(n2);
22253             } else {
22254                 var p = n2.parentNode;
22255                 var s = n2.nextSibling;
22256
22257                 if (s == n1) {
22258                     p.insertBefore(n1, n2);
22259                 } else if (n2 == n1.nextSibling) {
22260                     p.insertBefore(n2, n1);
22261                 } else {
22262                     n1.parentNode.replaceChild(n2, n1);
22263                     p.insertBefore(n1, s);
22264                 }
22265             }
22266         },
22267
22268         /**
22269          * Returns the current scroll position
22270          * @method getScroll
22271          * @private
22272          * @static
22273          */
22274         getScroll: function () {
22275             var t, l, dde=document.documentElement, db=document.body;
22276             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22277                 t = dde.scrollTop;
22278                 l = dde.scrollLeft;
22279             } else if (db) {
22280                 t = db.scrollTop;
22281                 l = db.scrollLeft;
22282             } else {
22283
22284             }
22285             return { top: t, left: l };
22286         },
22287
22288         /**
22289          * Returns the specified element style property
22290          * @method getStyle
22291          * @param {HTMLElement} el          the element
22292          * @param {string}      styleProp   the style property
22293          * @return {string} The value of the style property
22294          * @deprecated use Roo.lib.Dom.getStyle
22295          * @static
22296          */
22297         getStyle: function(el, styleProp) {
22298             return Roo.fly(el).getStyle(styleProp);
22299         },
22300
22301         /**
22302          * Gets the scrollTop
22303          * @method getScrollTop
22304          * @return {int} the document's scrollTop
22305          * @static
22306          */
22307         getScrollTop: function () { return this.getScroll().top; },
22308
22309         /**
22310          * Gets the scrollLeft
22311          * @method getScrollLeft
22312          * @return {int} the document's scrollTop
22313          * @static
22314          */
22315         getScrollLeft: function () { return this.getScroll().left; },
22316
22317         /**
22318          * Sets the x/y position of an element to the location of the
22319          * target element.
22320          * @method moveToEl
22321          * @param {HTMLElement} moveEl      The element to move
22322          * @param {HTMLElement} targetEl    The position reference element
22323          * @static
22324          */
22325         moveToEl: function (moveEl, targetEl) {
22326             var aCoord = Roo.lib.Dom.getXY(targetEl);
22327             Roo.lib.Dom.setXY(moveEl, aCoord);
22328         },
22329
22330         /**
22331          * Numeric array sort function
22332          * @method numericSort
22333          * @static
22334          */
22335         numericSort: function(a, b) { return (a - b); },
22336
22337         /**
22338          * Internal counter
22339          * @property _timeoutCount
22340          * @private
22341          * @static
22342          */
22343         _timeoutCount: 0,
22344
22345         /**
22346          * Trying to make the load order less important.  Without this we get
22347          * an error if this file is loaded before the Event Utility.
22348          * @method _addListeners
22349          * @private
22350          * @static
22351          */
22352         _addListeners: function() {
22353             var DDM = Roo.dd.DDM;
22354             if ( Roo.lib.Event && document ) {
22355                 DDM._onLoad();
22356             } else {
22357                 if (DDM._timeoutCount > 2000) {
22358                 } else {
22359                     setTimeout(DDM._addListeners, 10);
22360                     if (document && document.body) {
22361                         DDM._timeoutCount += 1;
22362                     }
22363                 }
22364             }
22365         },
22366
22367         /**
22368          * Recursively searches the immediate parent and all child nodes for
22369          * the handle element in order to determine wheter or not it was
22370          * clicked.
22371          * @method handleWasClicked
22372          * @param node the html element to inspect
22373          * @static
22374          */
22375         handleWasClicked: function(node, id) {
22376             if (this.isHandle(id, node.id)) {
22377                 return true;
22378             } else {
22379                 // check to see if this is a text node child of the one we want
22380                 var p = node.parentNode;
22381
22382                 while (p) {
22383                     if (this.isHandle(id, p.id)) {
22384                         return true;
22385                     } else {
22386                         p = p.parentNode;
22387                     }
22388                 }
22389             }
22390
22391             return false;
22392         }
22393
22394     };
22395
22396 }();
22397
22398 // shorter alias, save a few bytes
22399 Roo.dd.DDM = Roo.dd.DragDropMgr;
22400 Roo.dd.DDM._addListeners();
22401
22402 }/*
22403  * Based on:
22404  * Ext JS Library 1.1.1
22405  * Copyright(c) 2006-2007, Ext JS, LLC.
22406  *
22407  * Originally Released Under LGPL - original licence link has changed is not relivant.
22408  *
22409  * Fork - LGPL
22410  * <script type="text/javascript">
22411  */
22412
22413 /**
22414  * @class Roo.dd.DD
22415  * A DragDrop implementation where the linked element follows the
22416  * mouse cursor during a drag.
22417  * @extends Roo.dd.DragDrop
22418  * @constructor
22419  * @param {String} id the id of the linked element
22420  * @param {String} sGroup the group of related DragDrop items
22421  * @param {object} config an object containing configurable attributes
22422  *                Valid properties for DD:
22423  *                    scroll
22424  */
22425 Roo.dd.DD = function(id, sGroup, config) {
22426     if (id) {
22427         this.init(id, sGroup, config);
22428     }
22429 };
22430
22431 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22432
22433     /**
22434      * When set to true, the utility automatically tries to scroll the browser
22435      * window wehn a drag and drop element is dragged near the viewport boundary.
22436      * Defaults to true.
22437      * @property scroll
22438      * @type boolean
22439      */
22440     scroll: true,
22441
22442     /**
22443      * Sets the pointer offset to the distance between the linked element's top
22444      * left corner and the location the element was clicked
22445      * @method autoOffset
22446      * @param {int} iPageX the X coordinate of the click
22447      * @param {int} iPageY the Y coordinate of the click
22448      */
22449     autoOffset: function(iPageX, iPageY) {
22450         var x = iPageX - this.startPageX;
22451         var y = iPageY - this.startPageY;
22452         this.setDelta(x, y);
22453     },
22454
22455     /**
22456      * Sets the pointer offset.  You can call this directly to force the
22457      * offset to be in a particular location (e.g., pass in 0,0 to set it
22458      * to the center of the object)
22459      * @method setDelta
22460      * @param {int} iDeltaX the distance from the left
22461      * @param {int} iDeltaY the distance from the top
22462      */
22463     setDelta: function(iDeltaX, iDeltaY) {
22464         this.deltaX = iDeltaX;
22465         this.deltaY = iDeltaY;
22466     },
22467
22468     /**
22469      * Sets the drag element to the location of the mousedown or click event,
22470      * maintaining the cursor location relative to the location on the element
22471      * that was clicked.  Override this if you want to place the element in a
22472      * location other than where the cursor is.
22473      * @method setDragElPos
22474      * @param {int} iPageX the X coordinate of the mousedown or drag event
22475      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22476      */
22477     setDragElPos: function(iPageX, iPageY) {
22478         // the first time we do this, we are going to check to make sure
22479         // the element has css positioning
22480
22481         var el = this.getDragEl();
22482         this.alignElWithMouse(el, iPageX, iPageY);
22483     },
22484
22485     /**
22486      * Sets the element to the location of the mousedown or click event,
22487      * maintaining the cursor location relative to the location on the element
22488      * that was clicked.  Override this if you want to place the element in a
22489      * location other than where the cursor is.
22490      * @method alignElWithMouse
22491      * @param {HTMLElement} el the element to move
22492      * @param {int} iPageX the X coordinate of the mousedown or drag event
22493      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22494      */
22495     alignElWithMouse: function(el, iPageX, iPageY) {
22496         var oCoord = this.getTargetCoord(iPageX, iPageY);
22497         var fly = el.dom ? el : Roo.fly(el);
22498         if (!this.deltaSetXY) {
22499             var aCoord = [oCoord.x, oCoord.y];
22500             fly.setXY(aCoord);
22501             var newLeft = fly.getLeft(true);
22502             var newTop  = fly.getTop(true);
22503             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22504         } else {
22505             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22506         }
22507
22508         this.cachePosition(oCoord.x, oCoord.y);
22509         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22510         return oCoord;
22511     },
22512
22513     /**
22514      * Saves the most recent position so that we can reset the constraints and
22515      * tick marks on-demand.  We need to know this so that we can calculate the
22516      * number of pixels the element is offset from its original position.
22517      * @method cachePosition
22518      * @param iPageX the current x position (optional, this just makes it so we
22519      * don't have to look it up again)
22520      * @param iPageY the current y position (optional, this just makes it so we
22521      * don't have to look it up again)
22522      */
22523     cachePosition: function(iPageX, iPageY) {
22524         if (iPageX) {
22525             this.lastPageX = iPageX;
22526             this.lastPageY = iPageY;
22527         } else {
22528             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22529             this.lastPageX = aCoord[0];
22530             this.lastPageY = aCoord[1];
22531         }
22532     },
22533
22534     /**
22535      * Auto-scroll the window if the dragged object has been moved beyond the
22536      * visible window boundary.
22537      * @method autoScroll
22538      * @param {int} x the drag element's x position
22539      * @param {int} y the drag element's y position
22540      * @param {int} h the height of the drag element
22541      * @param {int} w the width of the drag element
22542      * @private
22543      */
22544     autoScroll: function(x, y, h, w) {
22545
22546         if (this.scroll) {
22547             // The client height
22548             var clientH = Roo.lib.Dom.getViewWidth();
22549
22550             // The client width
22551             var clientW = Roo.lib.Dom.getViewHeight();
22552
22553             // The amt scrolled down
22554             var st = this.DDM.getScrollTop();
22555
22556             // The amt scrolled right
22557             var sl = this.DDM.getScrollLeft();
22558
22559             // Location of the bottom of the element
22560             var bot = h + y;
22561
22562             // Location of the right of the element
22563             var right = w + x;
22564
22565             // The distance from the cursor to the bottom of the visible area,
22566             // adjusted so that we don't scroll if the cursor is beyond the
22567             // element drag constraints
22568             var toBot = (clientH + st - y - this.deltaY);
22569
22570             // The distance from the cursor to the right of the visible area
22571             var toRight = (clientW + sl - x - this.deltaX);
22572
22573
22574             // How close to the edge the cursor must be before we scroll
22575             // var thresh = (document.all) ? 100 : 40;
22576             var thresh = 40;
22577
22578             // How many pixels to scroll per autoscroll op.  This helps to reduce
22579             // clunky scrolling. IE is more sensitive about this ... it needs this
22580             // value to be higher.
22581             var scrAmt = (document.all) ? 80 : 30;
22582
22583             // Scroll down if we are near the bottom of the visible page and the
22584             // obj extends below the crease
22585             if ( bot > clientH && toBot < thresh ) {
22586                 window.scrollTo(sl, st + scrAmt);
22587             }
22588
22589             // Scroll up if the window is scrolled down and the top of the object
22590             // goes above the top border
22591             if ( y < st && st > 0 && y - st < thresh ) {
22592                 window.scrollTo(sl, st - scrAmt);
22593             }
22594
22595             // Scroll right if the obj is beyond the right border and the cursor is
22596             // near the border.
22597             if ( right > clientW && toRight < thresh ) {
22598                 window.scrollTo(sl + scrAmt, st);
22599             }
22600
22601             // Scroll left if the window has been scrolled to the right and the obj
22602             // extends past the left border
22603             if ( x < sl && sl > 0 && x - sl < thresh ) {
22604                 window.scrollTo(sl - scrAmt, st);
22605             }
22606         }
22607     },
22608
22609     /**
22610      * Finds the location the element should be placed if we want to move
22611      * it to where the mouse location less the click offset would place us.
22612      * @method getTargetCoord
22613      * @param {int} iPageX the X coordinate of the click
22614      * @param {int} iPageY the Y coordinate of the click
22615      * @return an object that contains the coordinates (Object.x and Object.y)
22616      * @private
22617      */
22618     getTargetCoord: function(iPageX, iPageY) {
22619
22620
22621         var x = iPageX - this.deltaX;
22622         var y = iPageY - this.deltaY;
22623
22624         if (this.constrainX) {
22625             if (x < this.minX) { x = this.minX; }
22626             if (x > this.maxX) { x = this.maxX; }
22627         }
22628
22629         if (this.constrainY) {
22630             if (y < this.minY) { y = this.minY; }
22631             if (y > this.maxY) { y = this.maxY; }
22632         }
22633
22634         x = this.getTick(x, this.xTicks);
22635         y = this.getTick(y, this.yTicks);
22636
22637
22638         return {x:x, y:y};
22639     },
22640
22641     /*
22642      * Sets up config options specific to this class. Overrides
22643      * Roo.dd.DragDrop, but all versions of this method through the
22644      * inheritance chain are called
22645      */
22646     applyConfig: function() {
22647         Roo.dd.DD.superclass.applyConfig.call(this);
22648         this.scroll = (this.config.scroll !== false);
22649     },
22650
22651     /*
22652      * Event that fires prior to the onMouseDown event.  Overrides
22653      * Roo.dd.DragDrop.
22654      */
22655     b4MouseDown: function(e) {
22656         // this.resetConstraints();
22657         this.autoOffset(e.getPageX(),
22658                             e.getPageY());
22659     },
22660
22661     /*
22662      * Event that fires prior to the onDrag event.  Overrides
22663      * Roo.dd.DragDrop.
22664      */
22665     b4Drag: function(e) {
22666         this.setDragElPos(e.getPageX(),
22667                             e.getPageY());
22668     },
22669
22670     toString: function() {
22671         return ("DD " + this.id);
22672     }
22673
22674     //////////////////////////////////////////////////////////////////////////
22675     // Debugging ygDragDrop events that can be overridden
22676     //////////////////////////////////////////////////////////////////////////
22677     /*
22678     startDrag: function(x, y) {
22679     },
22680
22681     onDrag: function(e) {
22682     },
22683
22684     onDragEnter: function(e, id) {
22685     },
22686
22687     onDragOver: function(e, id) {
22688     },
22689
22690     onDragOut: function(e, id) {
22691     },
22692
22693     onDragDrop: function(e, id) {
22694     },
22695
22696     endDrag: function(e) {
22697     }
22698
22699     */
22700
22701 });/*
22702  * Based on:
22703  * Ext JS Library 1.1.1
22704  * Copyright(c) 2006-2007, Ext JS, LLC.
22705  *
22706  * Originally Released Under LGPL - original licence link has changed is not relivant.
22707  *
22708  * Fork - LGPL
22709  * <script type="text/javascript">
22710  */
22711
22712 /**
22713  * @class Roo.dd.DDProxy
22714  * A DragDrop implementation that inserts an empty, bordered div into
22715  * the document that follows the cursor during drag operations.  At the time of
22716  * the click, the frame div is resized to the dimensions of the linked html
22717  * element, and moved to the exact location of the linked element.
22718  *
22719  * References to the "frame" element refer to the single proxy element that
22720  * was created to be dragged in place of all DDProxy elements on the
22721  * page.
22722  *
22723  * @extends Roo.dd.DD
22724  * @constructor
22725  * @param {String} id the id of the linked html element
22726  * @param {String} sGroup the group of related DragDrop objects
22727  * @param {object} config an object containing configurable attributes
22728  *                Valid properties for DDProxy in addition to those in DragDrop:
22729  *                   resizeFrame, centerFrame, dragElId
22730  */
22731 Roo.dd.DDProxy = function(id, sGroup, config) {
22732     if (id) {
22733         this.init(id, sGroup, config);
22734         this.initFrame();
22735     }
22736 };
22737
22738 /**
22739  * The default drag frame div id
22740  * @property Roo.dd.DDProxy.dragElId
22741  * @type String
22742  * @static
22743  */
22744 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22745
22746 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22747
22748     /**
22749      * By default we resize the drag frame to be the same size as the element
22750      * we want to drag (this is to get the frame effect).  We can turn it off
22751      * if we want a different behavior.
22752      * @property resizeFrame
22753      * @type boolean
22754      */
22755     resizeFrame: true,
22756
22757     /**
22758      * By default the frame is positioned exactly where the drag element is, so
22759      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22760      * you do not have constraints on the obj is to have the drag frame centered
22761      * around the cursor.  Set centerFrame to true for this effect.
22762      * @property centerFrame
22763      * @type boolean
22764      */
22765     centerFrame: false,
22766
22767     /**
22768      * Creates the proxy element if it does not yet exist
22769      * @method createFrame
22770      */
22771     createFrame: function() {
22772         var self = this;
22773         var body = document.body;
22774
22775         if (!body || !body.firstChild) {
22776             setTimeout( function() { self.createFrame(); }, 50 );
22777             return;
22778         }
22779
22780         var div = this.getDragEl();
22781
22782         if (!div) {
22783             div    = document.createElement("div");
22784             div.id = this.dragElId;
22785             var s  = div.style;
22786
22787             s.position   = "absolute";
22788             s.visibility = "hidden";
22789             s.cursor     = "move";
22790             s.border     = "2px solid #aaa";
22791             s.zIndex     = 999;
22792
22793             // appendChild can blow up IE if invoked prior to the window load event
22794             // while rendering a table.  It is possible there are other scenarios
22795             // that would cause this to happen as well.
22796             body.insertBefore(div, body.firstChild);
22797         }
22798     },
22799
22800     /**
22801      * Initialization for the drag frame element.  Must be called in the
22802      * constructor of all subclasses
22803      * @method initFrame
22804      */
22805     initFrame: function() {
22806         this.createFrame();
22807     },
22808
22809     applyConfig: function() {
22810         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22811
22812         this.resizeFrame = (this.config.resizeFrame !== false);
22813         this.centerFrame = (this.config.centerFrame);
22814         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22815     },
22816
22817     /**
22818      * Resizes the drag frame to the dimensions of the clicked object, positions
22819      * it over the object, and finally displays it
22820      * @method showFrame
22821      * @param {int} iPageX X click position
22822      * @param {int} iPageY Y click position
22823      * @private
22824      */
22825     showFrame: function(iPageX, iPageY) {
22826         var el = this.getEl();
22827         var dragEl = this.getDragEl();
22828         var s = dragEl.style;
22829
22830         this._resizeProxy();
22831
22832         if (this.centerFrame) {
22833             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22834                            Math.round(parseInt(s.height, 10)/2) );
22835         }
22836
22837         this.setDragElPos(iPageX, iPageY);
22838
22839         Roo.fly(dragEl).show();
22840     },
22841
22842     /**
22843      * The proxy is automatically resized to the dimensions of the linked
22844      * element when a drag is initiated, unless resizeFrame is set to false
22845      * @method _resizeProxy
22846      * @private
22847      */
22848     _resizeProxy: function() {
22849         if (this.resizeFrame) {
22850             var el = this.getEl();
22851             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22852         }
22853     },
22854
22855     // overrides Roo.dd.DragDrop
22856     b4MouseDown: function(e) {
22857         var x = e.getPageX();
22858         var y = e.getPageY();
22859         this.autoOffset(x, y);
22860         this.setDragElPos(x, y);
22861     },
22862
22863     // overrides Roo.dd.DragDrop
22864     b4StartDrag: function(x, y) {
22865         // show the drag frame
22866         this.showFrame(x, y);
22867     },
22868
22869     // overrides Roo.dd.DragDrop
22870     b4EndDrag: function(e) {
22871         Roo.fly(this.getDragEl()).hide();
22872     },
22873
22874     // overrides Roo.dd.DragDrop
22875     // By default we try to move the element to the last location of the frame.
22876     // This is so that the default behavior mirrors that of Roo.dd.DD.
22877     endDrag: function(e) {
22878
22879         var lel = this.getEl();
22880         var del = this.getDragEl();
22881
22882         // Show the drag frame briefly so we can get its position
22883         del.style.visibility = "";
22884
22885         this.beforeMove();
22886         // Hide the linked element before the move to get around a Safari
22887         // rendering bug.
22888         lel.style.visibility = "hidden";
22889         Roo.dd.DDM.moveToEl(lel, del);
22890         del.style.visibility = "hidden";
22891         lel.style.visibility = "";
22892
22893         this.afterDrag();
22894     },
22895
22896     beforeMove : function(){
22897
22898     },
22899
22900     afterDrag : function(){
22901
22902     },
22903
22904     toString: function() {
22905         return ("DDProxy " + this.id);
22906     }
22907
22908 });
22909 /*
22910  * Based on:
22911  * Ext JS Library 1.1.1
22912  * Copyright(c) 2006-2007, Ext JS, LLC.
22913  *
22914  * Originally Released Under LGPL - original licence link has changed is not relivant.
22915  *
22916  * Fork - LGPL
22917  * <script type="text/javascript">
22918  */
22919
22920  /**
22921  * @class Roo.dd.DDTarget
22922  * A DragDrop implementation that does not move, but can be a drop
22923  * target.  You would get the same result by simply omitting implementation
22924  * for the event callbacks, but this way we reduce the processing cost of the
22925  * event listener and the callbacks.
22926  * @extends Roo.dd.DragDrop
22927  * @constructor
22928  * @param {String} id the id of the element that is a drop target
22929  * @param {String} sGroup the group of related DragDrop objects
22930  * @param {object} config an object containing configurable attributes
22931  *                 Valid properties for DDTarget in addition to those in
22932  *                 DragDrop:
22933  *                    none
22934  */
22935 Roo.dd.DDTarget = function(id, sGroup, config) {
22936     if (id) {
22937         this.initTarget(id, sGroup, config);
22938     }
22939     if (config && (config.listeners || config.events)) { 
22940         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22941             listeners : config.listeners || {}, 
22942             events : config.events || {} 
22943         });    
22944     }
22945 };
22946
22947 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22948 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22949     toString: function() {
22950         return ("DDTarget " + this.id);
22951     }
22952 });
22953 /*
22954  * Based on:
22955  * Ext JS Library 1.1.1
22956  * Copyright(c) 2006-2007, Ext JS, LLC.
22957  *
22958  * Originally Released Under LGPL - original licence link has changed is not relivant.
22959  *
22960  * Fork - LGPL
22961  * <script type="text/javascript">
22962  */
22963  
22964
22965 /**
22966  * @class Roo.dd.ScrollManager
22967  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22968  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22969  * @static
22970  */
22971 Roo.dd.ScrollManager = function(){
22972     var ddm = Roo.dd.DragDropMgr;
22973     var els = {};
22974     var dragEl = null;
22975     var proc = {};
22976     
22977     
22978     
22979     var onStop = function(e){
22980         dragEl = null;
22981         clearProc();
22982     };
22983     
22984     var triggerRefresh = function(){
22985         if(ddm.dragCurrent){
22986              ddm.refreshCache(ddm.dragCurrent.groups);
22987         }
22988     };
22989     
22990     var doScroll = function(){
22991         if(ddm.dragCurrent){
22992             var dds = Roo.dd.ScrollManager;
22993             if(!dds.animate){
22994                 if(proc.el.scroll(proc.dir, dds.increment)){
22995                     triggerRefresh();
22996                 }
22997             }else{
22998                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22999             }
23000         }
23001     };
23002     
23003     var clearProc = function(){
23004         if(proc.id){
23005             clearInterval(proc.id);
23006         }
23007         proc.id = 0;
23008         proc.el = null;
23009         proc.dir = "";
23010     };
23011     
23012     var startProc = function(el, dir){
23013          Roo.log('scroll startproc');
23014         clearProc();
23015         proc.el = el;
23016         proc.dir = dir;
23017         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23018     };
23019     
23020     var onFire = function(e, isDrop){
23021        
23022         if(isDrop || !ddm.dragCurrent){ return; }
23023         var dds = Roo.dd.ScrollManager;
23024         if(!dragEl || dragEl != ddm.dragCurrent){
23025             dragEl = ddm.dragCurrent;
23026             // refresh regions on drag start
23027             dds.refreshCache();
23028         }
23029         
23030         var xy = Roo.lib.Event.getXY(e);
23031         var pt = new Roo.lib.Point(xy[0], xy[1]);
23032         for(var id in els){
23033             var el = els[id], r = el._region;
23034             if(r && r.contains(pt) && el.isScrollable()){
23035                 if(r.bottom - pt.y <= dds.thresh){
23036                     if(proc.el != el){
23037                         startProc(el, "down");
23038                     }
23039                     return;
23040                 }else if(r.right - pt.x <= dds.thresh){
23041                     if(proc.el != el){
23042                         startProc(el, "left");
23043                     }
23044                     return;
23045                 }else if(pt.y - r.top <= dds.thresh){
23046                     if(proc.el != el){
23047                         startProc(el, "up");
23048                     }
23049                     return;
23050                 }else if(pt.x - r.left <= dds.thresh){
23051                     if(proc.el != el){
23052                         startProc(el, "right");
23053                     }
23054                     return;
23055                 }
23056             }
23057         }
23058         clearProc();
23059     };
23060     
23061     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23062     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23063     
23064     return {
23065         /**
23066          * Registers new overflow element(s) to auto scroll
23067          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23068          */
23069         register : function(el){
23070             if(el instanceof Array){
23071                 for(var i = 0, len = el.length; i < len; i++) {
23072                         this.register(el[i]);
23073                 }
23074             }else{
23075                 el = Roo.get(el);
23076                 els[el.id] = el;
23077             }
23078             Roo.dd.ScrollManager.els = els;
23079         },
23080         
23081         /**
23082          * Unregisters overflow element(s) so they are no longer scrolled
23083          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23084          */
23085         unregister : function(el){
23086             if(el instanceof Array){
23087                 for(var i = 0, len = el.length; i < len; i++) {
23088                         this.unregister(el[i]);
23089                 }
23090             }else{
23091                 el = Roo.get(el);
23092                 delete els[el.id];
23093             }
23094         },
23095         
23096         /**
23097          * The number of pixels from the edge of a container the pointer needs to be to 
23098          * trigger scrolling (defaults to 25)
23099          * @type Number
23100          */
23101         thresh : 25,
23102         
23103         /**
23104          * The number of pixels to scroll in each scroll increment (defaults to 50)
23105          * @type Number
23106          */
23107         increment : 100,
23108         
23109         /**
23110          * The frequency of scrolls in milliseconds (defaults to 500)
23111          * @type Number
23112          */
23113         frequency : 500,
23114         
23115         /**
23116          * True to animate the scroll (defaults to true)
23117          * @type Boolean
23118          */
23119         animate: true,
23120         
23121         /**
23122          * The animation duration in seconds - 
23123          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23124          * @type Number
23125          */
23126         animDuration: .4,
23127         
23128         /**
23129          * Manually trigger a cache refresh.
23130          */
23131         refreshCache : function(){
23132             for(var id in els){
23133                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23134                     els[id]._region = els[id].getRegion();
23135                 }
23136             }
23137         }
23138     };
23139 }();/*
23140  * Based on:
23141  * Ext JS Library 1.1.1
23142  * Copyright(c) 2006-2007, Ext JS, LLC.
23143  *
23144  * Originally Released Under LGPL - original licence link has changed is not relivant.
23145  *
23146  * Fork - LGPL
23147  * <script type="text/javascript">
23148  */
23149  
23150
23151 /**
23152  * @class Roo.dd.Registry
23153  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23154  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23155  * @static
23156  */
23157 Roo.dd.Registry = function(){
23158     var elements = {}; 
23159     var handles = {}; 
23160     var autoIdSeed = 0;
23161
23162     var getId = function(el, autogen){
23163         if(typeof el == "string"){
23164             return el;
23165         }
23166         var id = el.id;
23167         if(!id && autogen !== false){
23168             id = "roodd-" + (++autoIdSeed);
23169             el.id = id;
23170         }
23171         return id;
23172     };
23173     
23174     return {
23175     /**
23176      * Register a drag drop element
23177      * @param {String|HTMLElement} element The id or DOM node to register
23178      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23179      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23180      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23181      * populated in the data object (if applicable):
23182      * <pre>
23183 Value      Description<br />
23184 ---------  ------------------------------------------<br />
23185 handles    Array of DOM nodes that trigger dragging<br />
23186            for the element being registered<br />
23187 isHandle   True if the element passed in triggers<br />
23188            dragging itself, else false
23189 </pre>
23190      */
23191         register : function(el, data){
23192             data = data || {};
23193             if(typeof el == "string"){
23194                 el = document.getElementById(el);
23195             }
23196             data.ddel = el;
23197             elements[getId(el)] = data;
23198             if(data.isHandle !== false){
23199                 handles[data.ddel.id] = data;
23200             }
23201             if(data.handles){
23202                 var hs = data.handles;
23203                 for(var i = 0, len = hs.length; i < len; i++){
23204                         handles[getId(hs[i])] = data;
23205                 }
23206             }
23207         },
23208
23209     /**
23210      * Unregister a drag drop element
23211      * @param {String|HTMLElement}  element The id or DOM node to unregister
23212      */
23213         unregister : function(el){
23214             var id = getId(el, false);
23215             var data = elements[id];
23216             if(data){
23217                 delete elements[id];
23218                 if(data.handles){
23219                     var hs = data.handles;
23220                     for(var i = 0, len = hs.length; i < len; i++){
23221                         delete handles[getId(hs[i], false)];
23222                     }
23223                 }
23224             }
23225         },
23226
23227     /**
23228      * Returns the handle registered for a DOM Node by id
23229      * @param {String|HTMLElement} id The DOM node or id to look up
23230      * @return {Object} handle The custom handle data
23231      */
23232         getHandle : function(id){
23233             if(typeof id != "string"){ // must be element?
23234                 id = id.id;
23235             }
23236             return handles[id];
23237         },
23238
23239     /**
23240      * Returns the handle that is registered for the DOM node that is the target of the event
23241      * @param {Event} e The event
23242      * @return {Object} handle The custom handle data
23243      */
23244         getHandleFromEvent : function(e){
23245             var t = Roo.lib.Event.getTarget(e);
23246             return t ? handles[t.id] : null;
23247         },
23248
23249     /**
23250      * Returns a custom data object that is registered for a DOM node by id
23251      * @param {String|HTMLElement} id The DOM node or id to look up
23252      * @return {Object} data The custom data
23253      */
23254         getTarget : function(id){
23255             if(typeof id != "string"){ // must be element?
23256                 id = id.id;
23257             }
23258             return elements[id];
23259         },
23260
23261     /**
23262      * Returns a custom data object that is registered for the DOM node that is the target of the event
23263      * @param {Event} e The event
23264      * @return {Object} data The custom data
23265      */
23266         getTargetFromEvent : function(e){
23267             var t = Roo.lib.Event.getTarget(e);
23268             return t ? elements[t.id] || handles[t.id] : null;
23269         }
23270     };
23271 }();/*
23272  * Based on:
23273  * Ext JS Library 1.1.1
23274  * Copyright(c) 2006-2007, Ext JS, LLC.
23275  *
23276  * Originally Released Under LGPL - original licence link has changed is not relivant.
23277  *
23278  * Fork - LGPL
23279  * <script type="text/javascript">
23280  */
23281  
23282
23283 /**
23284  * @class Roo.dd.StatusProxy
23285  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23286  * default drag proxy used by all Roo.dd components.
23287  * @constructor
23288  * @param {Object} config
23289  */
23290 Roo.dd.StatusProxy = function(config){
23291     Roo.apply(this, config);
23292     this.id = this.id || Roo.id();
23293     this.el = new Roo.Layer({
23294         dh: {
23295             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23296                 {tag: "div", cls: "x-dd-drop-icon"},
23297                 {tag: "div", cls: "x-dd-drag-ghost"}
23298             ]
23299         }, 
23300         shadow: !config || config.shadow !== false
23301     });
23302     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23303     this.dropStatus = this.dropNotAllowed;
23304 };
23305
23306 Roo.dd.StatusProxy.prototype = {
23307     /**
23308      * @cfg {String} dropAllowed
23309      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23310      */
23311     dropAllowed : "x-dd-drop-ok",
23312     /**
23313      * @cfg {String} dropNotAllowed
23314      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23315      */
23316     dropNotAllowed : "x-dd-drop-nodrop",
23317
23318     /**
23319      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23320      * over the current target element.
23321      * @param {String} cssClass The css class for the new drop status indicator image
23322      */
23323     setStatus : function(cssClass){
23324         cssClass = cssClass || this.dropNotAllowed;
23325         if(this.dropStatus != cssClass){
23326             this.el.replaceClass(this.dropStatus, cssClass);
23327             this.dropStatus = cssClass;
23328         }
23329     },
23330
23331     /**
23332      * Resets the status indicator to the default dropNotAllowed value
23333      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23334      */
23335     reset : function(clearGhost){
23336         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23337         this.dropStatus = this.dropNotAllowed;
23338         if(clearGhost){
23339             this.ghost.update("");
23340         }
23341     },
23342
23343     /**
23344      * Updates the contents of the ghost element
23345      * @param {String} html The html that will replace the current innerHTML of the ghost element
23346      */
23347     update : function(html){
23348         if(typeof html == "string"){
23349             this.ghost.update(html);
23350         }else{
23351             this.ghost.update("");
23352             html.style.margin = "0";
23353             this.ghost.dom.appendChild(html);
23354         }
23355         // ensure float = none set?? cant remember why though.
23356         var el = this.ghost.dom.firstChild;
23357                 if(el){
23358                         Roo.fly(el).setStyle('float', 'none');
23359                 }
23360     },
23361     
23362     /**
23363      * Returns the underlying proxy {@link Roo.Layer}
23364      * @return {Roo.Layer} el
23365     */
23366     getEl : function(){
23367         return this.el;
23368     },
23369
23370     /**
23371      * Returns the ghost element
23372      * @return {Roo.Element} el
23373      */
23374     getGhost : function(){
23375         return this.ghost;
23376     },
23377
23378     /**
23379      * Hides the proxy
23380      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23381      */
23382     hide : function(clear){
23383         this.el.hide();
23384         if(clear){
23385             this.reset(true);
23386         }
23387     },
23388
23389     /**
23390      * Stops the repair animation if it's currently running
23391      */
23392     stop : function(){
23393         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23394             this.anim.stop();
23395         }
23396     },
23397
23398     /**
23399      * Displays this proxy
23400      */
23401     show : function(){
23402         this.el.show();
23403     },
23404
23405     /**
23406      * Force the Layer to sync its shadow and shim positions to the element
23407      */
23408     sync : function(){
23409         this.el.sync();
23410     },
23411
23412     /**
23413      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23414      * invalid drop operation by the item being dragged.
23415      * @param {Array} xy The XY position of the element ([x, y])
23416      * @param {Function} callback The function to call after the repair is complete
23417      * @param {Object} scope The scope in which to execute the callback
23418      */
23419     repair : function(xy, callback, scope){
23420         this.callback = callback;
23421         this.scope = scope;
23422         if(xy && this.animRepair !== false){
23423             this.el.addClass("x-dd-drag-repair");
23424             this.el.hideUnders(true);
23425             this.anim = this.el.shift({
23426                 duration: this.repairDuration || .5,
23427                 easing: 'easeOut',
23428                 xy: xy,
23429                 stopFx: true,
23430                 callback: this.afterRepair,
23431                 scope: this
23432             });
23433         }else{
23434             this.afterRepair();
23435         }
23436     },
23437
23438     // private
23439     afterRepair : function(){
23440         this.hide(true);
23441         if(typeof this.callback == "function"){
23442             this.callback.call(this.scope || this);
23443         }
23444         this.callback = null;
23445         this.scope = null;
23446     }
23447 };/*
23448  * Based on:
23449  * Ext JS Library 1.1.1
23450  * Copyright(c) 2006-2007, Ext JS, LLC.
23451  *
23452  * Originally Released Under LGPL - original licence link has changed is not relivant.
23453  *
23454  * Fork - LGPL
23455  * <script type="text/javascript">
23456  */
23457
23458 /**
23459  * @class Roo.dd.DragSource
23460  * @extends Roo.dd.DDProxy
23461  * A simple class that provides the basic implementation needed to make any element draggable.
23462  * @constructor
23463  * @param {String/HTMLElement/Element} el The container element
23464  * @param {Object} config
23465  */
23466 Roo.dd.DragSource = function(el, config){
23467     this.el = Roo.get(el);
23468     this.dragData = {};
23469     
23470     Roo.apply(this, config);
23471     
23472     if(!this.proxy){
23473         this.proxy = new Roo.dd.StatusProxy();
23474     }
23475
23476     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23477           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23478     
23479     this.dragging = false;
23480 };
23481
23482 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23483     /**
23484      * @cfg {String} dropAllowed
23485      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23486      */
23487     dropAllowed : "x-dd-drop-ok",
23488     /**
23489      * @cfg {String} dropNotAllowed
23490      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23491      */
23492     dropNotAllowed : "x-dd-drop-nodrop",
23493
23494     /**
23495      * Returns the data object associated with this drag source
23496      * @return {Object} data An object containing arbitrary data
23497      */
23498     getDragData : function(e){
23499         return this.dragData;
23500     },
23501
23502     // private
23503     onDragEnter : function(e, id){
23504         var target = Roo.dd.DragDropMgr.getDDById(id);
23505         this.cachedTarget = target;
23506         if(this.beforeDragEnter(target, e, id) !== false){
23507             if(target.isNotifyTarget){
23508                 var status = target.notifyEnter(this, e, this.dragData);
23509                 this.proxy.setStatus(status);
23510             }else{
23511                 this.proxy.setStatus(this.dropAllowed);
23512             }
23513             
23514             if(this.afterDragEnter){
23515                 /**
23516                  * An empty function by default, but provided so that you can perform a custom action
23517                  * when the dragged item enters the drop target by providing an implementation.
23518                  * @param {Roo.dd.DragDrop} target The drop target
23519                  * @param {Event} e The event object
23520                  * @param {String} id The id of the dragged element
23521                  * @method afterDragEnter
23522                  */
23523                 this.afterDragEnter(target, e, id);
23524             }
23525         }
23526     },
23527
23528     /**
23529      * An empty function by default, but provided so that you can perform a custom action
23530      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23531      * @param {Roo.dd.DragDrop} target The drop target
23532      * @param {Event} e The event object
23533      * @param {String} id The id of the dragged element
23534      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23535      */
23536     beforeDragEnter : function(target, e, id){
23537         return true;
23538     },
23539
23540     // private
23541     alignElWithMouse: function() {
23542         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23543         this.proxy.sync();
23544     },
23545
23546     // private
23547     onDragOver : function(e, id){
23548         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23549         if(this.beforeDragOver(target, e, id) !== false){
23550             if(target.isNotifyTarget){
23551                 var status = target.notifyOver(this, e, this.dragData);
23552                 this.proxy.setStatus(status);
23553             }
23554
23555             if(this.afterDragOver){
23556                 /**
23557                  * An empty function by default, but provided so that you can perform a custom action
23558                  * while the dragged item is over the drop target by providing an implementation.
23559                  * @param {Roo.dd.DragDrop} target The drop target
23560                  * @param {Event} e The event object
23561                  * @param {String} id The id of the dragged element
23562                  * @method afterDragOver
23563                  */
23564                 this.afterDragOver(target, e, id);
23565             }
23566         }
23567     },
23568
23569     /**
23570      * An empty function by default, but provided so that you can perform a custom action
23571      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23572      * @param {Roo.dd.DragDrop} target The drop target
23573      * @param {Event} e The event object
23574      * @param {String} id The id of the dragged element
23575      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23576      */
23577     beforeDragOver : function(target, e, id){
23578         return true;
23579     },
23580
23581     // private
23582     onDragOut : function(e, id){
23583         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23584         if(this.beforeDragOut(target, e, id) !== false){
23585             if(target.isNotifyTarget){
23586                 target.notifyOut(this, e, this.dragData);
23587             }
23588             this.proxy.reset();
23589             if(this.afterDragOut){
23590                 /**
23591                  * An empty function by default, but provided so that you can perform a custom action
23592                  * after the dragged item is dragged out of the target without dropping.
23593                  * @param {Roo.dd.DragDrop} target The drop target
23594                  * @param {Event} e The event object
23595                  * @param {String} id The id of the dragged element
23596                  * @method afterDragOut
23597                  */
23598                 this.afterDragOut(target, e, id);
23599             }
23600         }
23601         this.cachedTarget = null;
23602     },
23603
23604     /**
23605      * An empty function by default, but provided so that you can perform a custom action before the dragged
23606      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23607      * @param {Roo.dd.DragDrop} target The drop target
23608      * @param {Event} e The event object
23609      * @param {String} id The id of the dragged element
23610      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23611      */
23612     beforeDragOut : function(target, e, id){
23613         return true;
23614     },
23615     
23616     // private
23617     onDragDrop : function(e, id){
23618         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23619         if(this.beforeDragDrop(target, e, id) !== false){
23620             if(target.isNotifyTarget){
23621                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23622                     this.onValidDrop(target, e, id);
23623                 }else{
23624                     this.onInvalidDrop(target, e, id);
23625                 }
23626             }else{
23627                 this.onValidDrop(target, e, id);
23628             }
23629             
23630             if(this.afterDragDrop){
23631                 /**
23632                  * An empty function by default, but provided so that you can perform a custom action
23633                  * after a valid drag drop has occurred by providing an implementation.
23634                  * @param {Roo.dd.DragDrop} target The drop target
23635                  * @param {Event} e The event object
23636                  * @param {String} id The id of the dropped element
23637                  * @method afterDragDrop
23638                  */
23639                 this.afterDragDrop(target, e, id);
23640             }
23641         }
23642         delete this.cachedTarget;
23643     },
23644
23645     /**
23646      * An empty function by default, but provided so that you can perform a custom action before the dragged
23647      * item is dropped onto the target and optionally cancel the onDragDrop.
23648      * @param {Roo.dd.DragDrop} target The drop target
23649      * @param {Event} e The event object
23650      * @param {String} id The id of the dragged element
23651      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23652      */
23653     beforeDragDrop : function(target, e, id){
23654         return true;
23655     },
23656
23657     // private
23658     onValidDrop : function(target, e, id){
23659         this.hideProxy();
23660         if(this.afterValidDrop){
23661             /**
23662              * An empty function by default, but provided so that you can perform a custom action
23663              * after a valid drop has occurred by providing an implementation.
23664              * @param {Object} target The target DD 
23665              * @param {Event} e The event object
23666              * @param {String} id The id of the dropped element
23667              * @method afterInvalidDrop
23668              */
23669             this.afterValidDrop(target, e, id);
23670         }
23671     },
23672
23673     // private
23674     getRepairXY : function(e, data){
23675         return this.el.getXY();  
23676     },
23677
23678     // private
23679     onInvalidDrop : function(target, e, id){
23680         this.beforeInvalidDrop(target, e, id);
23681         if(this.cachedTarget){
23682             if(this.cachedTarget.isNotifyTarget){
23683                 this.cachedTarget.notifyOut(this, e, this.dragData);
23684             }
23685             this.cacheTarget = null;
23686         }
23687         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23688
23689         if(this.afterInvalidDrop){
23690             /**
23691              * An empty function by default, but provided so that you can perform a custom action
23692              * after an invalid drop has occurred by providing an implementation.
23693              * @param {Event} e The event object
23694              * @param {String} id The id of the dropped element
23695              * @method afterInvalidDrop
23696              */
23697             this.afterInvalidDrop(e, id);
23698         }
23699     },
23700
23701     // private
23702     afterRepair : function(){
23703         if(Roo.enableFx){
23704             this.el.highlight(this.hlColor || "c3daf9");
23705         }
23706         this.dragging = false;
23707     },
23708
23709     /**
23710      * An empty function by default, but provided so that you can perform a custom action after an invalid
23711      * drop has occurred.
23712      * @param {Roo.dd.DragDrop} target The drop target
23713      * @param {Event} e The event object
23714      * @param {String} id The id of the dragged element
23715      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23716      */
23717     beforeInvalidDrop : function(target, e, id){
23718         return true;
23719     },
23720
23721     // private
23722     handleMouseDown : function(e){
23723         if(this.dragging) {
23724             return;
23725         }
23726         var data = this.getDragData(e);
23727         if(data && this.onBeforeDrag(data, e) !== false){
23728             this.dragData = data;
23729             this.proxy.stop();
23730             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23731         } 
23732     },
23733
23734     /**
23735      * An empty function by default, but provided so that you can perform a custom action before the initial
23736      * drag event begins and optionally cancel it.
23737      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23738      * @param {Event} e The event object
23739      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23740      */
23741     onBeforeDrag : function(data, e){
23742         return true;
23743     },
23744
23745     /**
23746      * An empty function by default, but provided so that you can perform a custom action once the initial
23747      * drag event has begun.  The drag cannot be canceled from this function.
23748      * @param {Number} x The x position of the click on the dragged object
23749      * @param {Number} y The y position of the click on the dragged object
23750      */
23751     onStartDrag : Roo.emptyFn,
23752
23753     // private - YUI override
23754     startDrag : function(x, y){
23755         this.proxy.reset();
23756         this.dragging = true;
23757         this.proxy.update("");
23758         this.onInitDrag(x, y);
23759         this.proxy.show();
23760     },
23761
23762     // private
23763     onInitDrag : function(x, y){
23764         var clone = this.el.dom.cloneNode(true);
23765         clone.id = Roo.id(); // prevent duplicate ids
23766         this.proxy.update(clone);
23767         this.onStartDrag(x, y);
23768         return true;
23769     },
23770
23771     /**
23772      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23773      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23774      */
23775     getProxy : function(){
23776         return this.proxy;  
23777     },
23778
23779     /**
23780      * Hides the drag source's {@link Roo.dd.StatusProxy}
23781      */
23782     hideProxy : function(){
23783         this.proxy.hide();  
23784         this.proxy.reset(true);
23785         this.dragging = false;
23786     },
23787
23788     // private
23789     triggerCacheRefresh : function(){
23790         Roo.dd.DDM.refreshCache(this.groups);
23791     },
23792
23793     // private - override to prevent hiding
23794     b4EndDrag: function(e) {
23795     },
23796
23797     // private - override to prevent moving
23798     endDrag : function(e){
23799         this.onEndDrag(this.dragData, e);
23800     },
23801
23802     // private
23803     onEndDrag : function(data, e){
23804     },
23805     
23806     // private - pin to cursor
23807     autoOffset : function(x, y) {
23808         this.setDelta(-12, -20);
23809     }    
23810 });/*
23811  * Based on:
23812  * Ext JS Library 1.1.1
23813  * Copyright(c) 2006-2007, Ext JS, LLC.
23814  *
23815  * Originally Released Under LGPL - original licence link has changed is not relivant.
23816  *
23817  * Fork - LGPL
23818  * <script type="text/javascript">
23819  */
23820
23821
23822 /**
23823  * @class Roo.dd.DropTarget
23824  * @extends Roo.dd.DDTarget
23825  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23826  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23827  * @constructor
23828  * @param {String/HTMLElement/Element} el The container element
23829  * @param {Object} config
23830  */
23831 Roo.dd.DropTarget = function(el, config){
23832     this.el = Roo.get(el);
23833     
23834     var listeners = false; ;
23835     if (config && config.listeners) {
23836         listeners= config.listeners;
23837         delete config.listeners;
23838     }
23839     Roo.apply(this, config);
23840     
23841     if(this.containerScroll){
23842         Roo.dd.ScrollManager.register(this.el);
23843     }
23844     this.addEvents( {
23845          /**
23846          * @scope Roo.dd.DropTarget
23847          */
23848          
23849          /**
23850          * @event enter
23851          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23852          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23853          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23854          * 
23855          * IMPORTANT : it should set  this.valid to true|false
23856          * 
23857          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23858          * @param {Event} e The event
23859          * @param {Object} data An object containing arbitrary data supplied by the drag source
23860          */
23861         "enter" : true,
23862         
23863          /**
23864          * @event over
23865          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23866          * This method will be called on every mouse movement while the drag source is over the drop target.
23867          * This default implementation simply returns the dropAllowed config value.
23868          * 
23869          * IMPORTANT : it should set  this.valid to true|false
23870          * 
23871          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23872          * @param {Event} e The event
23873          * @param {Object} data An object containing arbitrary data supplied by the drag source
23874          
23875          */
23876         "over" : true,
23877         /**
23878          * @event out
23879          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23880          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23881          * overClass (if any) from the drop element.
23882          * 
23883          * 
23884          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23885          * @param {Event} e The event
23886          * @param {Object} data An object containing arbitrary data supplied by the drag source
23887          */
23888          "out" : true,
23889          
23890         /**
23891          * @event drop
23892          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23893          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23894          * implementation that does something to process the drop event and returns true so that the drag source's
23895          * repair action does not run.
23896          * 
23897          * IMPORTANT : it should set this.success
23898          * 
23899          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23900          * @param {Event} e The event
23901          * @param {Object} data An object containing arbitrary data supplied by the drag source
23902         */
23903          "drop" : true
23904     });
23905             
23906      
23907     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23908         this.el.dom, 
23909         this.ddGroup || this.group,
23910         {
23911             isTarget: true,
23912             listeners : listeners || {} 
23913            
23914         
23915         }
23916     );
23917
23918 };
23919
23920 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23921     /**
23922      * @cfg {String} overClass
23923      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23924      */
23925      /**
23926      * @cfg {String} ddGroup
23927      * The drag drop group to handle drop events for
23928      */
23929      
23930     /**
23931      * @cfg {String} dropAllowed
23932      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23933      */
23934     dropAllowed : "x-dd-drop-ok",
23935     /**
23936      * @cfg {String} dropNotAllowed
23937      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23938      */
23939     dropNotAllowed : "x-dd-drop-nodrop",
23940     /**
23941      * @cfg {boolean} success
23942      * set this after drop listener.. 
23943      */
23944     success : false,
23945     /**
23946      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23947      * if the drop point is valid for over/enter..
23948      */
23949     valid : false,
23950     // private
23951     isTarget : true,
23952
23953     // private
23954     isNotifyTarget : true,
23955     
23956     /**
23957      * @hide
23958      */
23959     notifyEnter : function(dd, e, data)
23960     {
23961         this.valid = true;
23962         this.fireEvent('enter', dd, e, data);
23963         if(this.overClass){
23964             this.el.addClass(this.overClass);
23965         }
23966         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23967             this.valid ? this.dropAllowed : this.dropNotAllowed
23968         );
23969     },
23970
23971     /**
23972      * @hide
23973      */
23974     notifyOver : function(dd, e, data)
23975     {
23976         this.valid = true;
23977         this.fireEvent('over', dd, e, data);
23978         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23979             this.valid ? this.dropAllowed : this.dropNotAllowed
23980         );
23981     },
23982
23983     /**
23984      * @hide
23985      */
23986     notifyOut : function(dd, e, data)
23987     {
23988         this.fireEvent('out', dd, e, data);
23989         if(this.overClass){
23990             this.el.removeClass(this.overClass);
23991         }
23992     },
23993
23994     /**
23995      * @hide
23996      */
23997     notifyDrop : function(dd, e, data)
23998     {
23999         this.success = false;
24000         this.fireEvent('drop', dd, e, data);
24001         return this.success;
24002     }
24003 });/*
24004  * Based on:
24005  * Ext JS Library 1.1.1
24006  * Copyright(c) 2006-2007, Ext JS, LLC.
24007  *
24008  * Originally Released Under LGPL - original licence link has changed is not relivant.
24009  *
24010  * Fork - LGPL
24011  * <script type="text/javascript">
24012  */
24013
24014
24015 /**
24016  * @class Roo.dd.DragZone
24017  * @extends Roo.dd.DragSource
24018  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24019  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24020  * @constructor
24021  * @param {String/HTMLElement/Element} el The container element
24022  * @param {Object} config
24023  */
24024 Roo.dd.DragZone = function(el, config){
24025     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24026     if(this.containerScroll){
24027         Roo.dd.ScrollManager.register(this.el);
24028     }
24029 };
24030
24031 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24032     /**
24033      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24034      * for auto scrolling during drag operations.
24035      */
24036     /**
24037      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24038      * method after a failed drop (defaults to "c3daf9" - light blue)
24039      */
24040
24041     /**
24042      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24043      * for a valid target to drag based on the mouse down. Override this method
24044      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24045      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24046      * @param {EventObject} e The mouse down event
24047      * @return {Object} The dragData
24048      */
24049     getDragData : function(e){
24050         return Roo.dd.Registry.getHandleFromEvent(e);
24051     },
24052     
24053     /**
24054      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24055      * this.dragData.ddel
24056      * @param {Number} x The x position of the click on the dragged object
24057      * @param {Number} y The y position of the click on the dragged object
24058      * @return {Boolean} true to continue the drag, false to cancel
24059      */
24060     onInitDrag : function(x, y){
24061         this.proxy.update(this.dragData.ddel.cloneNode(true));
24062         this.onStartDrag(x, y);
24063         return true;
24064     },
24065     
24066     /**
24067      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24068      */
24069     afterRepair : function(){
24070         if(Roo.enableFx){
24071             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24072         }
24073         this.dragging = false;
24074     },
24075
24076     /**
24077      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24078      * the XY of this.dragData.ddel
24079      * @param {EventObject} e The mouse up event
24080      * @return {Array} The xy location (e.g. [100, 200])
24081      */
24082     getRepairXY : function(e){
24083         return Roo.Element.fly(this.dragData.ddel).getXY();  
24084     }
24085 });/*
24086  * Based on:
24087  * Ext JS Library 1.1.1
24088  * Copyright(c) 2006-2007, Ext JS, LLC.
24089  *
24090  * Originally Released Under LGPL - original licence link has changed is not relivant.
24091  *
24092  * Fork - LGPL
24093  * <script type="text/javascript">
24094  */
24095 /**
24096  * @class Roo.dd.DropZone
24097  * @extends Roo.dd.DropTarget
24098  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24099  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24100  * @constructor
24101  * @param {String/HTMLElement/Element} el The container element
24102  * @param {Object} config
24103  */
24104 Roo.dd.DropZone = function(el, config){
24105     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24106 };
24107
24108 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24109     /**
24110      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24111      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24112      * provide your own custom lookup.
24113      * @param {Event} e The event
24114      * @return {Object} data The custom data
24115      */
24116     getTargetFromEvent : function(e){
24117         return Roo.dd.Registry.getTargetFromEvent(e);
24118     },
24119
24120     /**
24121      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24122      * that it has registered.  This method has no default implementation and should be overridden to provide
24123      * node-specific processing if necessary.
24124      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24125      * {@link #getTargetFromEvent} for this node)
24126      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24127      * @param {Event} e The event
24128      * @param {Object} data An object containing arbitrary data supplied by the drag source
24129      */
24130     onNodeEnter : function(n, dd, e, data){
24131         
24132     },
24133
24134     /**
24135      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24136      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24137      * overridden to provide the proper feedback.
24138      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24139      * {@link #getTargetFromEvent} for this node)
24140      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24141      * @param {Event} e The event
24142      * @param {Object} data An object containing arbitrary data supplied by the drag source
24143      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24144      * underlying {@link Roo.dd.StatusProxy} can be updated
24145      */
24146     onNodeOver : function(n, dd, e, data){
24147         return this.dropAllowed;
24148     },
24149
24150     /**
24151      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24152      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24153      * node-specific processing if necessary.
24154      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24155      * {@link #getTargetFromEvent} for this node)
24156      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24157      * @param {Event} e The event
24158      * @param {Object} data An object containing arbitrary data supplied by the drag source
24159      */
24160     onNodeOut : function(n, dd, e, data){
24161         
24162     },
24163
24164     /**
24165      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24166      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24167      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24168      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24169      * {@link #getTargetFromEvent} for this node)
24170      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24171      * @param {Event} e The event
24172      * @param {Object} data An object containing arbitrary data supplied by the drag source
24173      * @return {Boolean} True if the drop was valid, else false
24174      */
24175     onNodeDrop : function(n, dd, e, data){
24176         return false;
24177     },
24178
24179     /**
24180      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24181      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24182      * it should be overridden to provide the proper feedback if necessary.
24183      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24184      * @param {Event} e The event
24185      * @param {Object} data An object containing arbitrary data supplied by the drag source
24186      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24187      * underlying {@link Roo.dd.StatusProxy} can be updated
24188      */
24189     onContainerOver : function(dd, e, data){
24190         return this.dropNotAllowed;
24191     },
24192
24193     /**
24194      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24195      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24196      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24197      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24198      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24199      * @param {Event} e The event
24200      * @param {Object} data An object containing arbitrary data supplied by the drag source
24201      * @return {Boolean} True if the drop was valid, else false
24202      */
24203     onContainerDrop : function(dd, e, data){
24204         return false;
24205     },
24206
24207     /**
24208      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24209      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24210      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24211      * you should override this method and provide a custom implementation.
24212      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24213      * @param {Event} e The event
24214      * @param {Object} data An object containing arbitrary data supplied by the drag source
24215      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24216      * underlying {@link Roo.dd.StatusProxy} can be updated
24217      */
24218     notifyEnter : function(dd, e, data){
24219         return this.dropNotAllowed;
24220     },
24221
24222     /**
24223      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24224      * This method will be called on every mouse movement while the drag source is over the drop zone.
24225      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24226      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24227      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24228      * registered node, it will call {@link #onContainerOver}.
24229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24230      * @param {Event} e The event
24231      * @param {Object} data An object containing arbitrary data supplied by the drag source
24232      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24233      * underlying {@link Roo.dd.StatusProxy} can be updated
24234      */
24235     notifyOver : function(dd, e, data){
24236         var n = this.getTargetFromEvent(e);
24237         if(!n){ // not over valid drop target
24238             if(this.lastOverNode){
24239                 this.onNodeOut(this.lastOverNode, dd, e, data);
24240                 this.lastOverNode = null;
24241             }
24242             return this.onContainerOver(dd, e, data);
24243         }
24244         if(this.lastOverNode != n){
24245             if(this.lastOverNode){
24246                 this.onNodeOut(this.lastOverNode, dd, e, data);
24247             }
24248             this.onNodeEnter(n, dd, e, data);
24249             this.lastOverNode = n;
24250         }
24251         return this.onNodeOver(n, dd, e, data);
24252     },
24253
24254     /**
24255      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24256      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24257      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24258      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24259      * @param {Event} e The event
24260      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24261      */
24262     notifyOut : function(dd, e, data){
24263         if(this.lastOverNode){
24264             this.onNodeOut(this.lastOverNode, dd, e, data);
24265             this.lastOverNode = null;
24266         }
24267     },
24268
24269     /**
24270      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24271      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24272      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24273      * otherwise it will call {@link #onContainerDrop}.
24274      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24275      * @param {Event} e The event
24276      * @param {Object} data An object containing arbitrary data supplied by the drag source
24277      * @return {Boolean} True if the drop was valid, else false
24278      */
24279     notifyDrop : function(dd, e, data){
24280         if(this.lastOverNode){
24281             this.onNodeOut(this.lastOverNode, dd, e, data);
24282             this.lastOverNode = null;
24283         }
24284         var n = this.getTargetFromEvent(e);
24285         return n ?
24286             this.onNodeDrop(n, dd, e, data) :
24287             this.onContainerDrop(dd, e, data);
24288     },
24289
24290     // private
24291     triggerCacheRefresh : function(){
24292         Roo.dd.DDM.refreshCache(this.groups);
24293     }  
24294 });/*
24295  * Based on:
24296  * Ext JS Library 1.1.1
24297  * Copyright(c) 2006-2007, Ext JS, LLC.
24298  *
24299  * Originally Released Under LGPL - original licence link has changed is not relivant.
24300  *
24301  * Fork - LGPL
24302  * <script type="text/javascript">
24303  */
24304
24305
24306 /**
24307  * @class Roo.data.SortTypes
24308  * @static
24309  * Defines the default sorting (casting?) comparison functions used when sorting data.
24310  */
24311 Roo.data.SortTypes = {
24312     /**
24313      * Default sort that does nothing
24314      * @param {Mixed} s The value being converted
24315      * @return {Mixed} The comparison value
24316      */
24317     none : function(s){
24318         return s;
24319     },
24320     
24321     /**
24322      * The regular expression used to strip tags
24323      * @type {RegExp}
24324      * @property
24325      */
24326     stripTagsRE : /<\/?[^>]+>/gi,
24327     
24328     /**
24329      * Strips all HTML tags to sort on text only
24330      * @param {Mixed} s The value being converted
24331      * @return {String} The comparison value
24332      */
24333     asText : function(s){
24334         return String(s).replace(this.stripTagsRE, "");
24335     },
24336     
24337     /**
24338      * Strips all HTML tags to sort on text only - Case insensitive
24339      * @param {Mixed} s The value being converted
24340      * @return {String} The comparison value
24341      */
24342     asUCText : function(s){
24343         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24344     },
24345     
24346     /**
24347      * Case insensitive string
24348      * @param {Mixed} s The value being converted
24349      * @return {String} The comparison value
24350      */
24351     asUCString : function(s) {
24352         return String(s).toUpperCase();
24353     },
24354     
24355     /**
24356      * Date sorting
24357      * @param {Mixed} s The value being converted
24358      * @return {Number} The comparison value
24359      */
24360     asDate : function(s) {
24361         if(!s){
24362             return 0;
24363         }
24364         if(s instanceof Date){
24365             return s.getTime();
24366         }
24367         return Date.parse(String(s));
24368     },
24369     
24370     /**
24371      * Float sorting
24372      * @param {Mixed} s The value being converted
24373      * @return {Float} The comparison value
24374      */
24375     asFloat : function(s) {
24376         var val = parseFloat(String(s).replace(/,/g, ""));
24377         if(isNaN(val)) {
24378             val = 0;
24379         }
24380         return val;
24381     },
24382     
24383     /**
24384      * Integer sorting
24385      * @param {Mixed} s The value being converted
24386      * @return {Number} The comparison value
24387      */
24388     asInt : function(s) {
24389         var val = parseInt(String(s).replace(/,/g, ""));
24390         if(isNaN(val)) {
24391             val = 0;
24392         }
24393         return val;
24394     }
24395 };/*
24396  * Based on:
24397  * Ext JS Library 1.1.1
24398  * Copyright(c) 2006-2007, Ext JS, LLC.
24399  *
24400  * Originally Released Under LGPL - original licence link has changed is not relivant.
24401  *
24402  * Fork - LGPL
24403  * <script type="text/javascript">
24404  */
24405
24406 /**
24407 * @class Roo.data.Record
24408  * Instances of this class encapsulate both record <em>definition</em> information, and record
24409  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24410  * to access Records cached in an {@link Roo.data.Store} object.<br>
24411  * <p>
24412  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24413  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24414  * objects.<br>
24415  * <p>
24416  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24417  * @constructor
24418  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24419  * {@link #create}. The parameters are the same.
24420  * @param {Array} data An associative Array of data values keyed by the field name.
24421  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24422  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24423  * not specified an integer id is generated.
24424  */
24425 Roo.data.Record = function(data, id){
24426     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24427     this.data = data;
24428 };
24429
24430 /**
24431  * Generate a constructor for a specific record layout.
24432  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24433  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24434  * Each field definition object may contain the following properties: <ul>
24435  * <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,
24436  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24437  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24438  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24439  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24440  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24441  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24442  * this may be omitted.</p></li>
24443  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24444  * <ul><li>auto (Default, implies no conversion)</li>
24445  * <li>string</li>
24446  * <li>int</li>
24447  * <li>float</li>
24448  * <li>boolean</li>
24449  * <li>date</li></ul></p></li>
24450  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24451  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24452  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24453  * by the Reader into an object that will be stored in the Record. It is passed the
24454  * following parameters:<ul>
24455  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24456  * </ul></p></li>
24457  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24458  * </ul>
24459  * <br>usage:<br><pre><code>
24460 var TopicRecord = Roo.data.Record.create(
24461     {name: 'title', mapping: 'topic_title'},
24462     {name: 'author', mapping: 'username'},
24463     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24464     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24465     {name: 'lastPoster', mapping: 'user2'},
24466     {name: 'excerpt', mapping: 'post_text'}
24467 );
24468
24469 var myNewRecord = new TopicRecord({
24470     title: 'Do my job please',
24471     author: 'noobie',
24472     totalPosts: 1,
24473     lastPost: new Date(),
24474     lastPoster: 'Animal',
24475     excerpt: 'No way dude!'
24476 });
24477 myStore.add(myNewRecord);
24478 </code></pre>
24479  * @method create
24480  * @static
24481  */
24482 Roo.data.Record.create = function(o){
24483     var f = function(){
24484         f.superclass.constructor.apply(this, arguments);
24485     };
24486     Roo.extend(f, Roo.data.Record);
24487     var p = f.prototype;
24488     p.fields = new Roo.util.MixedCollection(false, function(field){
24489         return field.name;
24490     });
24491     for(var i = 0, len = o.length; i < len; i++){
24492         p.fields.add(new Roo.data.Field(o[i]));
24493     }
24494     f.getField = function(name){
24495         return p.fields.get(name);  
24496     };
24497     return f;
24498 };
24499
24500 Roo.data.Record.AUTO_ID = 1000;
24501 Roo.data.Record.EDIT = 'edit';
24502 Roo.data.Record.REJECT = 'reject';
24503 Roo.data.Record.COMMIT = 'commit';
24504
24505 Roo.data.Record.prototype = {
24506     /**
24507      * Readonly flag - true if this record has been modified.
24508      * @type Boolean
24509      */
24510     dirty : false,
24511     editing : false,
24512     error: null,
24513     modified: null,
24514
24515     // private
24516     join : function(store){
24517         this.store = store;
24518     },
24519
24520     /**
24521      * Set the named field to the specified value.
24522      * @param {String} name The name of the field to set.
24523      * @param {Object} value The value to set the field to.
24524      */
24525     set : function(name, value){
24526         if(this.data[name] == value){
24527             return;
24528         }
24529         this.dirty = true;
24530         if(!this.modified){
24531             this.modified = {};
24532         }
24533         if(typeof this.modified[name] == 'undefined'){
24534             this.modified[name] = this.data[name];
24535         }
24536         this.data[name] = value;
24537         if(!this.editing && this.store){
24538             this.store.afterEdit(this);
24539         }       
24540     },
24541
24542     /**
24543      * Get the value of the named field.
24544      * @param {String} name The name of the field to get the value of.
24545      * @return {Object} The value of the field.
24546      */
24547     get : function(name){
24548         return this.data[name]; 
24549     },
24550
24551     // private
24552     beginEdit : function(){
24553         this.editing = true;
24554         this.modified = {}; 
24555     },
24556
24557     // private
24558     cancelEdit : function(){
24559         this.editing = false;
24560         delete this.modified;
24561     },
24562
24563     // private
24564     endEdit : function(){
24565         this.editing = false;
24566         if(this.dirty && this.store){
24567             this.store.afterEdit(this);
24568         }
24569     },
24570
24571     /**
24572      * Usually called by the {@link Roo.data.Store} which owns the Record.
24573      * Rejects all changes made to the Record since either creation, or the last commit operation.
24574      * Modified fields are reverted to their original values.
24575      * <p>
24576      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24577      * of reject operations.
24578      */
24579     reject : function(){
24580         var m = this.modified;
24581         for(var n in m){
24582             if(typeof m[n] != "function"){
24583                 this.data[n] = m[n];
24584             }
24585         }
24586         this.dirty = false;
24587         delete this.modified;
24588         this.editing = false;
24589         if(this.store){
24590             this.store.afterReject(this);
24591         }
24592     },
24593
24594     /**
24595      * Usually called by the {@link Roo.data.Store} which owns the Record.
24596      * Commits all changes made to the Record since either creation, or the last commit operation.
24597      * <p>
24598      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24599      * of commit operations.
24600      */
24601     commit : function(){
24602         this.dirty = false;
24603         delete this.modified;
24604         this.editing = false;
24605         if(this.store){
24606             this.store.afterCommit(this);
24607         }
24608     },
24609
24610     // private
24611     hasError : function(){
24612         return this.error != null;
24613     },
24614
24615     // private
24616     clearError : function(){
24617         this.error = null;
24618     },
24619
24620     /**
24621      * Creates a copy of this record.
24622      * @param {String} id (optional) A new record id if you don't want to use this record's id
24623      * @return {Record}
24624      */
24625     copy : function(newId) {
24626         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24627     }
24628 };/*
24629  * Based on:
24630  * Ext JS Library 1.1.1
24631  * Copyright(c) 2006-2007, Ext JS, LLC.
24632  *
24633  * Originally Released Under LGPL - original licence link has changed is not relivant.
24634  *
24635  * Fork - LGPL
24636  * <script type="text/javascript">
24637  */
24638
24639
24640
24641 /**
24642  * @class Roo.data.Store
24643  * @extends Roo.util.Observable
24644  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24645  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24646  * <p>
24647  * 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
24648  * has no knowledge of the format of the data returned by the Proxy.<br>
24649  * <p>
24650  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24651  * instances from the data object. These records are cached and made available through accessor functions.
24652  * @constructor
24653  * Creates a new Store.
24654  * @param {Object} config A config object containing the objects needed for the Store to access data,
24655  * and read the data into Records.
24656  */
24657 Roo.data.Store = function(config){
24658     this.data = new Roo.util.MixedCollection(false);
24659     this.data.getKey = function(o){
24660         return o.id;
24661     };
24662     this.baseParams = {};
24663     // private
24664     this.paramNames = {
24665         "start" : "start",
24666         "limit" : "limit",
24667         "sort" : "sort",
24668         "dir" : "dir",
24669         "multisort" : "_multisort"
24670     };
24671
24672     if(config && config.data){
24673         this.inlineData = config.data;
24674         delete config.data;
24675     }
24676
24677     Roo.apply(this, config);
24678     
24679     if(this.reader){ // reader passed
24680         this.reader = Roo.factory(this.reader, Roo.data);
24681         this.reader.xmodule = this.xmodule || false;
24682         if(!this.recordType){
24683             this.recordType = this.reader.recordType;
24684         }
24685         if(this.reader.onMetaChange){
24686             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24687         }
24688     }
24689
24690     if(this.recordType){
24691         this.fields = this.recordType.prototype.fields;
24692     }
24693     this.modified = [];
24694
24695     this.addEvents({
24696         /**
24697          * @event datachanged
24698          * Fires when the data cache has changed, and a widget which is using this Store
24699          * as a Record cache should refresh its view.
24700          * @param {Store} this
24701          */
24702         datachanged : true,
24703         /**
24704          * @event metachange
24705          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24706          * @param {Store} this
24707          * @param {Object} meta The JSON metadata
24708          */
24709         metachange : true,
24710         /**
24711          * @event add
24712          * Fires when Records have been added to the Store
24713          * @param {Store} this
24714          * @param {Roo.data.Record[]} records The array of Records added
24715          * @param {Number} index The index at which the record(s) were added
24716          */
24717         add : true,
24718         /**
24719          * @event remove
24720          * Fires when a Record has been removed from the Store
24721          * @param {Store} this
24722          * @param {Roo.data.Record} record The Record that was removed
24723          * @param {Number} index The index at which the record was removed
24724          */
24725         remove : true,
24726         /**
24727          * @event update
24728          * Fires when a Record has been updated
24729          * @param {Store} this
24730          * @param {Roo.data.Record} record The Record that was updated
24731          * @param {String} operation The update operation being performed.  Value may be one of:
24732          * <pre><code>
24733  Roo.data.Record.EDIT
24734  Roo.data.Record.REJECT
24735  Roo.data.Record.COMMIT
24736          * </code></pre>
24737          */
24738         update : true,
24739         /**
24740          * @event clear
24741          * Fires when the data cache has been cleared.
24742          * @param {Store} this
24743          */
24744         clear : true,
24745         /**
24746          * @event beforeload
24747          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24748          * the load action will be canceled.
24749          * @param {Store} this
24750          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24751          */
24752         beforeload : true,
24753         /**
24754          * @event beforeloadadd
24755          * Fires after a new set of Records has been loaded.
24756          * @param {Store} this
24757          * @param {Roo.data.Record[]} records The Records that were loaded
24758          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24759          */
24760         beforeloadadd : true,
24761         /**
24762          * @event load
24763          * Fires after a new set of Records has been loaded, before they are added to the store.
24764          * @param {Store} this
24765          * @param {Roo.data.Record[]} records The Records that were loaded
24766          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24767          * @params {Object} return from reader
24768          */
24769         load : true,
24770         /**
24771          * @event loadexception
24772          * Fires if an exception occurs in the Proxy during loading.
24773          * Called with the signature of the Proxy's "loadexception" event.
24774          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24775          * 
24776          * @param {Proxy} 
24777          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24778          * @param {Object} load options 
24779          * @param {Object} jsonData from your request (normally this contains the Exception)
24780          */
24781         loadexception : true
24782     });
24783     
24784     if(this.proxy){
24785         this.proxy = Roo.factory(this.proxy, Roo.data);
24786         this.proxy.xmodule = this.xmodule || false;
24787         this.relayEvents(this.proxy,  ["loadexception"]);
24788     }
24789     this.sortToggle = {};
24790     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24791
24792     Roo.data.Store.superclass.constructor.call(this);
24793
24794     if(this.inlineData){
24795         this.loadData(this.inlineData);
24796         delete this.inlineData;
24797     }
24798 };
24799
24800 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24801      /**
24802     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24803     * without a remote query - used by combo/forms at present.
24804     */
24805     
24806     /**
24807     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24808     */
24809     /**
24810     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24811     */
24812     /**
24813     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24814     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24815     */
24816     /**
24817     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24818     * on any HTTP request
24819     */
24820     /**
24821     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24822     */
24823     /**
24824     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24825     */
24826     multiSort: false,
24827     /**
24828     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24829     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24830     */
24831     remoteSort : false,
24832
24833     /**
24834     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24835      * loaded or when a record is removed. (defaults to false).
24836     */
24837     pruneModifiedRecords : false,
24838
24839     // private
24840     lastOptions : null,
24841
24842     /**
24843      * Add Records to the Store and fires the add event.
24844      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24845      */
24846     add : function(records){
24847         records = [].concat(records);
24848         for(var i = 0, len = records.length; i < len; i++){
24849             records[i].join(this);
24850         }
24851         var index = this.data.length;
24852         this.data.addAll(records);
24853         this.fireEvent("add", this, records, index);
24854     },
24855
24856     /**
24857      * Remove a Record from the Store and fires the remove event.
24858      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24859      */
24860     remove : function(record){
24861         var index = this.data.indexOf(record);
24862         this.data.removeAt(index);
24863  
24864         if(this.pruneModifiedRecords){
24865             this.modified.remove(record);
24866         }
24867         this.fireEvent("remove", this, record, index);
24868     },
24869
24870     /**
24871      * Remove all Records from the Store and fires the clear event.
24872      */
24873     removeAll : function(){
24874         this.data.clear();
24875         if(this.pruneModifiedRecords){
24876             this.modified = [];
24877         }
24878         this.fireEvent("clear", this);
24879     },
24880
24881     /**
24882      * Inserts Records to the Store at the given index and fires the add event.
24883      * @param {Number} index The start index at which to insert the passed Records.
24884      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24885      */
24886     insert : function(index, records){
24887         records = [].concat(records);
24888         for(var i = 0, len = records.length; i < len; i++){
24889             this.data.insert(index, records[i]);
24890             records[i].join(this);
24891         }
24892         this.fireEvent("add", this, records, index);
24893     },
24894
24895     /**
24896      * Get the index within the cache of the passed Record.
24897      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24898      * @return {Number} The index of the passed Record. Returns -1 if not found.
24899      */
24900     indexOf : function(record){
24901         return this.data.indexOf(record);
24902     },
24903
24904     /**
24905      * Get the index within the cache of the Record with the passed id.
24906      * @param {String} id The id of the Record to find.
24907      * @return {Number} The index of the Record. Returns -1 if not found.
24908      */
24909     indexOfId : function(id){
24910         return this.data.indexOfKey(id);
24911     },
24912
24913     /**
24914      * Get the Record with the specified id.
24915      * @param {String} id The id of the Record to find.
24916      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24917      */
24918     getById : function(id){
24919         return this.data.key(id);
24920     },
24921
24922     /**
24923      * Get the Record at the specified index.
24924      * @param {Number} index The index of the Record to find.
24925      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24926      */
24927     getAt : function(index){
24928         return this.data.itemAt(index);
24929     },
24930
24931     /**
24932      * Returns a range of Records between specified indices.
24933      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24934      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24935      * @return {Roo.data.Record[]} An array of Records
24936      */
24937     getRange : function(start, end){
24938         return this.data.getRange(start, end);
24939     },
24940
24941     // private
24942     storeOptions : function(o){
24943         o = Roo.apply({}, o);
24944         delete o.callback;
24945         delete o.scope;
24946         this.lastOptions = o;
24947     },
24948
24949     /**
24950      * Loads the Record cache from the configured Proxy using the configured Reader.
24951      * <p>
24952      * If using remote paging, then the first load call must specify the <em>start</em>
24953      * and <em>limit</em> properties in the options.params property to establish the initial
24954      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24955      * <p>
24956      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24957      * and this call will return before the new data has been loaded. Perform any post-processing
24958      * in a callback function, or in a "load" event handler.</strong>
24959      * <p>
24960      * @param {Object} options An object containing properties which control loading options:<ul>
24961      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24962      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24963      * <pre>
24964                 {
24965                     data : data,  // array of key=>value data like JsonReader
24966                     total : data.length,
24967                     success : true
24968                     
24969                 }
24970         </pre>
24971             }.</li>
24972      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24973      * passed the following arguments:<ul>
24974      * <li>r : Roo.data.Record[]</li>
24975      * <li>options: Options object from the load call</li>
24976      * <li>success: Boolean success indicator</li></ul></li>
24977      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24978      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24979      * </ul>
24980      */
24981     load : function(options){
24982         options = options || {};
24983         if(this.fireEvent("beforeload", this, options) !== false){
24984             this.storeOptions(options);
24985             var p = Roo.apply(options.params || {}, this.baseParams);
24986             // if meta was not loaded from remote source.. try requesting it.
24987             if (!this.reader.metaFromRemote) {
24988                 p._requestMeta = 1;
24989             }
24990             if(this.sortInfo && this.remoteSort){
24991                 var pn = this.paramNames;
24992                 p[pn["sort"]] = this.sortInfo.field;
24993                 p[pn["dir"]] = this.sortInfo.direction;
24994             }
24995             if (this.multiSort) {
24996                 var pn = this.paramNames;
24997                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24998             }
24999             
25000             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25001         }
25002     },
25003
25004     /**
25005      * Reloads the Record cache from the configured Proxy using the configured Reader and
25006      * the options from the last load operation performed.
25007      * @param {Object} options (optional) An object containing properties which may override the options
25008      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25009      * the most recently used options are reused).
25010      */
25011     reload : function(options){
25012         this.load(Roo.applyIf(options||{}, this.lastOptions));
25013     },
25014
25015     // private
25016     // Called as a callback by the Reader during a load operation.
25017     loadRecords : function(o, options, success){
25018          
25019         if(!o){
25020             if(success !== false){
25021                 this.fireEvent("load", this, [], options, o);
25022             }
25023             if(options.callback){
25024                 options.callback.call(options.scope || this, [], options, false);
25025             }
25026             return;
25027         }
25028         // if data returned failure - throw an exception.
25029         if (o.success === false) {
25030             // show a message if no listener is registered.
25031             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25032                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25033             }
25034             // loadmask wil be hooked into this..
25035             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25036             return;
25037         }
25038         var r = o.records, t = o.totalRecords || r.length;
25039         
25040         this.fireEvent("beforeloadadd", this, r, options, o);
25041         
25042         if(!options || options.add !== true){
25043             if(this.pruneModifiedRecords){
25044                 this.modified = [];
25045             }
25046             for(var i = 0, len = r.length; i < len; i++){
25047                 r[i].join(this);
25048             }
25049             if(this.snapshot){
25050                 this.data = this.snapshot;
25051                 delete this.snapshot;
25052             }
25053             this.data.clear();
25054             this.data.addAll(r);
25055             this.totalLength = t;
25056             this.applySort();
25057             this.fireEvent("datachanged", this);
25058         }else{
25059             this.totalLength = Math.max(t, this.data.length+r.length);
25060             this.add(r);
25061         }
25062         
25063         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25064                 
25065             var e = new Roo.data.Record({});
25066
25067             e.set(this.parent.displayField, this.parent.emptyTitle);
25068             e.set(this.parent.valueField, '');
25069
25070             this.insert(0, e);
25071         }
25072             
25073         this.fireEvent("load", this, r, options, o);
25074         if(options.callback){
25075             options.callback.call(options.scope || this, r, options, true);
25076         }
25077     },
25078
25079
25080     /**
25081      * Loads data from a passed data block. A Reader which understands the format of the data
25082      * must have been configured in the constructor.
25083      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25084      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25085      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25086      */
25087     loadData : function(o, append){
25088         var r = this.reader.readRecords(o);
25089         this.loadRecords(r, {add: append}, true);
25090     },
25091     
25092      /**
25093      * using 'cn' the nested child reader read the child array into it's child stores.
25094      * @param {Object} rec The record with a 'children array
25095      */
25096     loadDataFromChildren : function(rec)
25097     {
25098         this.loadData(this.reader.toLoadData(rec));
25099     },
25100     
25101
25102     /**
25103      * Gets the number of cached records.
25104      * <p>
25105      * <em>If using paging, this may not be the total size of the dataset. If the data object
25106      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25107      * the data set size</em>
25108      */
25109     getCount : function(){
25110         return this.data.length || 0;
25111     },
25112
25113     /**
25114      * Gets the total number of records in the dataset as returned by the server.
25115      * <p>
25116      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25117      * the dataset size</em>
25118      */
25119     getTotalCount : function(){
25120         return this.totalLength || 0;
25121     },
25122
25123     /**
25124      * Returns the sort state of the Store as an object with two properties:
25125      * <pre><code>
25126  field {String} The name of the field by which the Records are sorted
25127  direction {String} The sort order, "ASC" or "DESC"
25128      * </code></pre>
25129      */
25130     getSortState : function(){
25131         return this.sortInfo;
25132     },
25133
25134     // private
25135     applySort : function(){
25136         if(this.sortInfo && !this.remoteSort){
25137             var s = this.sortInfo, f = s.field;
25138             var st = this.fields.get(f).sortType;
25139             var fn = function(r1, r2){
25140                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25141                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25142             };
25143             this.data.sort(s.direction, fn);
25144             if(this.snapshot && this.snapshot != this.data){
25145                 this.snapshot.sort(s.direction, fn);
25146             }
25147         }
25148     },
25149
25150     /**
25151      * Sets the default sort column and order to be used by the next load operation.
25152      * @param {String} fieldName The name of the field to sort by.
25153      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25154      */
25155     setDefaultSort : function(field, dir){
25156         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25157     },
25158
25159     /**
25160      * Sort the Records.
25161      * If remote sorting is used, the sort is performed on the server, and the cache is
25162      * reloaded. If local sorting is used, the cache is sorted internally.
25163      * @param {String} fieldName The name of the field to sort by.
25164      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25165      */
25166     sort : function(fieldName, dir){
25167         var f = this.fields.get(fieldName);
25168         if(!dir){
25169             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25170             
25171             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25172                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25173             }else{
25174                 dir = f.sortDir;
25175             }
25176         }
25177         this.sortToggle[f.name] = dir;
25178         this.sortInfo = {field: f.name, direction: dir};
25179         if(!this.remoteSort){
25180             this.applySort();
25181             this.fireEvent("datachanged", this);
25182         }else{
25183             this.load(this.lastOptions);
25184         }
25185     },
25186
25187     /**
25188      * Calls the specified function for each of the Records in the cache.
25189      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25190      * Returning <em>false</em> aborts and exits the iteration.
25191      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25192      */
25193     each : function(fn, scope){
25194         this.data.each(fn, scope);
25195     },
25196
25197     /**
25198      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25199      * (e.g., during paging).
25200      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25201      */
25202     getModifiedRecords : function(){
25203         return this.modified;
25204     },
25205
25206     // private
25207     createFilterFn : function(property, value, anyMatch){
25208         if(!value.exec){ // not a regex
25209             value = String(value);
25210             if(value.length == 0){
25211                 return false;
25212             }
25213             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25214         }
25215         return function(r){
25216             return value.test(r.data[property]);
25217         };
25218     },
25219
25220     /**
25221      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25222      * @param {String} property A field on your records
25223      * @param {Number} start The record index to start at (defaults to 0)
25224      * @param {Number} end The last record index to include (defaults to length - 1)
25225      * @return {Number} The sum
25226      */
25227     sum : function(property, start, end){
25228         var rs = this.data.items, v = 0;
25229         start = start || 0;
25230         end = (end || end === 0) ? end : rs.length-1;
25231
25232         for(var i = start; i <= end; i++){
25233             v += (rs[i].data[property] || 0);
25234         }
25235         return v;
25236     },
25237
25238     /**
25239      * Filter the records by a specified property.
25240      * @param {String} field A field on your records
25241      * @param {String/RegExp} value Either a string that the field
25242      * should start with or a RegExp to test against the field
25243      * @param {Boolean} anyMatch True to match any part not just the beginning
25244      */
25245     filter : function(property, value, anyMatch){
25246         var fn = this.createFilterFn(property, value, anyMatch);
25247         return fn ? this.filterBy(fn) : this.clearFilter();
25248     },
25249
25250     /**
25251      * Filter by a function. The specified function will be called with each
25252      * record in this data source. If the function returns true the record is included,
25253      * otherwise it is filtered.
25254      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25255      * @param {Object} scope (optional) The scope of the function (defaults to this)
25256      */
25257     filterBy : function(fn, scope){
25258         this.snapshot = this.snapshot || this.data;
25259         this.data = this.queryBy(fn, scope||this);
25260         this.fireEvent("datachanged", this);
25261     },
25262
25263     /**
25264      * Query the records by a specified property.
25265      * @param {String} field A field on your records
25266      * @param {String/RegExp} value Either a string that the field
25267      * should start with or a RegExp to test against the field
25268      * @param {Boolean} anyMatch True to match any part not just the beginning
25269      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25270      */
25271     query : function(property, value, anyMatch){
25272         var fn = this.createFilterFn(property, value, anyMatch);
25273         return fn ? this.queryBy(fn) : this.data.clone();
25274     },
25275
25276     /**
25277      * Query by a function. The specified function will be called with each
25278      * record in this data source. If the function returns true the record is included
25279      * in the results.
25280      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25281      * @param {Object} scope (optional) The scope of the function (defaults to this)
25282       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25283      **/
25284     queryBy : function(fn, scope){
25285         var data = this.snapshot || this.data;
25286         return data.filterBy(fn, scope||this);
25287     },
25288
25289     /**
25290      * Collects unique values for a particular dataIndex from this store.
25291      * @param {String} dataIndex The property to collect
25292      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25293      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25294      * @return {Array} An array of the unique values
25295      **/
25296     collect : function(dataIndex, allowNull, bypassFilter){
25297         var d = (bypassFilter === true && this.snapshot) ?
25298                 this.snapshot.items : this.data.items;
25299         var v, sv, r = [], l = {};
25300         for(var i = 0, len = d.length; i < len; i++){
25301             v = d[i].data[dataIndex];
25302             sv = String(v);
25303             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25304                 l[sv] = true;
25305                 r[r.length] = v;
25306             }
25307         }
25308         return r;
25309     },
25310
25311     /**
25312      * Revert to a view of the Record cache with no filtering applied.
25313      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25314      */
25315     clearFilter : function(suppressEvent){
25316         if(this.snapshot && this.snapshot != this.data){
25317             this.data = this.snapshot;
25318             delete this.snapshot;
25319             if(suppressEvent !== true){
25320                 this.fireEvent("datachanged", this);
25321             }
25322         }
25323     },
25324
25325     // private
25326     afterEdit : function(record){
25327         if(this.modified.indexOf(record) == -1){
25328             this.modified.push(record);
25329         }
25330         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25331     },
25332     
25333     // private
25334     afterReject : function(record){
25335         this.modified.remove(record);
25336         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25337     },
25338
25339     // private
25340     afterCommit : function(record){
25341         this.modified.remove(record);
25342         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25343     },
25344
25345     /**
25346      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25347      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25348      */
25349     commitChanges : function(){
25350         var m = this.modified.slice(0);
25351         this.modified = [];
25352         for(var i = 0, len = m.length; i < len; i++){
25353             m[i].commit();
25354         }
25355     },
25356
25357     /**
25358      * Cancel outstanding changes on all changed records.
25359      */
25360     rejectChanges : function(){
25361         var m = this.modified.slice(0);
25362         this.modified = [];
25363         for(var i = 0, len = m.length; i < len; i++){
25364             m[i].reject();
25365         }
25366     },
25367
25368     onMetaChange : function(meta, rtype, o){
25369         this.recordType = rtype;
25370         this.fields = rtype.prototype.fields;
25371         delete this.snapshot;
25372         this.sortInfo = meta.sortInfo || this.sortInfo;
25373         this.modified = [];
25374         this.fireEvent('metachange', this, this.reader.meta);
25375     },
25376     
25377     moveIndex : function(data, type)
25378     {
25379         var index = this.indexOf(data);
25380         
25381         var newIndex = index + type;
25382         
25383         this.remove(data);
25384         
25385         this.insert(newIndex, data);
25386         
25387     }
25388 });/*
25389  * Based on:
25390  * Ext JS Library 1.1.1
25391  * Copyright(c) 2006-2007, Ext JS, LLC.
25392  *
25393  * Originally Released Under LGPL - original licence link has changed is not relivant.
25394  *
25395  * Fork - LGPL
25396  * <script type="text/javascript">
25397  */
25398
25399 /**
25400  * @class Roo.data.SimpleStore
25401  * @extends Roo.data.Store
25402  * Small helper class to make creating Stores from Array data easier.
25403  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25404  * @cfg {Array} fields An array of field definition objects, or field name strings.
25405  * @cfg {Object} an existing reader (eg. copied from another store)
25406  * @cfg {Array} data The multi-dimensional array of data
25407  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25408  * @cfg {Roo.data.Reader} reader  [not-required] 
25409  * @constructor
25410  * @param {Object} config
25411  */
25412 Roo.data.SimpleStore = function(config)
25413 {
25414     Roo.data.SimpleStore.superclass.constructor.call(this, {
25415         isLocal : true,
25416         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25417                 id: config.id
25418             },
25419             Roo.data.Record.create(config.fields)
25420         ),
25421         proxy : new Roo.data.MemoryProxy(config.data)
25422     });
25423     this.load();
25424 };
25425 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25426  * Based on:
25427  * Ext JS Library 1.1.1
25428  * Copyright(c) 2006-2007, Ext JS, LLC.
25429  *
25430  * Originally Released Under LGPL - original licence link has changed is not relivant.
25431  *
25432  * Fork - LGPL
25433  * <script type="text/javascript">
25434  */
25435
25436 /**
25437 /**
25438  * @extends Roo.data.Store
25439  * @class Roo.data.JsonStore
25440  * Small helper class to make creating Stores for JSON data easier. <br/>
25441 <pre><code>
25442 var store = new Roo.data.JsonStore({
25443     url: 'get-images.php',
25444     root: 'images',
25445     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25446 });
25447 </code></pre>
25448  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25449  * JsonReader and HttpProxy (unless inline data is provided).</b>
25450  * @cfg {Array} fields An array of field definition objects, or field name strings.
25451  * @constructor
25452  * @param {Object} config
25453  */
25454 Roo.data.JsonStore = function(c){
25455     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25456         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25457         reader: new Roo.data.JsonReader(c, c.fields)
25458     }));
25459 };
25460 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25461  * Based on:
25462  * Ext JS Library 1.1.1
25463  * Copyright(c) 2006-2007, Ext JS, LLC.
25464  *
25465  * Originally Released Under LGPL - original licence link has changed is not relivant.
25466  *
25467  * Fork - LGPL
25468  * <script type="text/javascript">
25469  */
25470
25471  
25472 Roo.data.Field = function(config){
25473     if(typeof config == "string"){
25474         config = {name: config};
25475     }
25476     Roo.apply(this, config);
25477     
25478     if(!this.type){
25479         this.type = "auto";
25480     }
25481     
25482     var st = Roo.data.SortTypes;
25483     // named sortTypes are supported, here we look them up
25484     if(typeof this.sortType == "string"){
25485         this.sortType = st[this.sortType];
25486     }
25487     
25488     // set default sortType for strings and dates
25489     if(!this.sortType){
25490         switch(this.type){
25491             case "string":
25492                 this.sortType = st.asUCString;
25493                 break;
25494             case "date":
25495                 this.sortType = st.asDate;
25496                 break;
25497             default:
25498                 this.sortType = st.none;
25499         }
25500     }
25501
25502     // define once
25503     var stripRe = /[\$,%]/g;
25504
25505     // prebuilt conversion function for this field, instead of
25506     // switching every time we're reading a value
25507     if(!this.convert){
25508         var cv, dateFormat = this.dateFormat;
25509         switch(this.type){
25510             case "":
25511             case "auto":
25512             case undefined:
25513                 cv = function(v){ return v; };
25514                 break;
25515             case "string":
25516                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25517                 break;
25518             case "int":
25519                 cv = function(v){
25520                     return v !== undefined && v !== null && v !== '' ?
25521                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25522                     };
25523                 break;
25524             case "float":
25525                 cv = function(v){
25526                     return v !== undefined && v !== null && v !== '' ?
25527                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25528                     };
25529                 break;
25530             case "bool":
25531             case "boolean":
25532                 cv = function(v){ return v === true || v === "true" || v == 1; };
25533                 break;
25534             case "date":
25535                 cv = function(v){
25536                     if(!v){
25537                         return '';
25538                     }
25539                     if(v instanceof Date){
25540                         return v;
25541                     }
25542                     if(dateFormat){
25543                         if(dateFormat == "timestamp"){
25544                             return new Date(v*1000);
25545                         }
25546                         return Date.parseDate(v, dateFormat);
25547                     }
25548                     var parsed = Date.parse(v);
25549                     return parsed ? new Date(parsed) : null;
25550                 };
25551              break;
25552             
25553         }
25554         this.convert = cv;
25555     }
25556 };
25557
25558 Roo.data.Field.prototype = {
25559     dateFormat: null,
25560     defaultValue: "",
25561     mapping: null,
25562     sortType : null,
25563     sortDir : "ASC"
25564 };/*
25565  * Based on:
25566  * Ext JS Library 1.1.1
25567  * Copyright(c) 2006-2007, Ext JS, LLC.
25568  *
25569  * Originally Released Under LGPL - original licence link has changed is not relivant.
25570  *
25571  * Fork - LGPL
25572  * <script type="text/javascript">
25573  */
25574  
25575 // Base class for reading structured data from a data source.  This class is intended to be
25576 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25577
25578 /**
25579  * @class Roo.data.DataReader
25580  * @abstract
25581  * Base class for reading structured data from a data source.  This class is intended to be
25582  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25583  */
25584
25585 Roo.data.DataReader = function(meta, recordType){
25586     
25587     this.meta = meta;
25588     
25589     this.recordType = recordType instanceof Array ? 
25590         Roo.data.Record.create(recordType) : recordType;
25591 };
25592
25593 Roo.data.DataReader.prototype = {
25594     
25595     
25596     readerType : 'Data',
25597      /**
25598      * Create an empty record
25599      * @param {Object} data (optional) - overlay some values
25600      * @return {Roo.data.Record} record created.
25601      */
25602     newRow :  function(d) {
25603         var da =  {};
25604         this.recordType.prototype.fields.each(function(c) {
25605             switch( c.type) {
25606                 case 'int' : da[c.name] = 0; break;
25607                 case 'date' : da[c.name] = new Date(); break;
25608                 case 'float' : da[c.name] = 0.0; break;
25609                 case 'boolean' : da[c.name] = false; break;
25610                 default : da[c.name] = ""; break;
25611             }
25612             
25613         });
25614         return new this.recordType(Roo.apply(da, d));
25615     }
25616     
25617     
25618 };/*
25619  * Based on:
25620  * Ext JS Library 1.1.1
25621  * Copyright(c) 2006-2007, Ext JS, LLC.
25622  *
25623  * Originally Released Under LGPL - original licence link has changed is not relivant.
25624  *
25625  * Fork - LGPL
25626  * <script type="text/javascript">
25627  */
25628
25629 /**
25630  * @class Roo.data.DataProxy
25631  * @extends Roo.util.Observable
25632  * @abstract
25633  * This class is an abstract base class for implementations which provide retrieval of
25634  * unformatted data objects.<br>
25635  * <p>
25636  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25637  * (of the appropriate type which knows how to parse the data object) to provide a block of
25638  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25639  * <p>
25640  * Custom implementations must implement the load method as described in
25641  * {@link Roo.data.HttpProxy#load}.
25642  */
25643 Roo.data.DataProxy = function(){
25644     this.addEvents({
25645         /**
25646          * @event beforeload
25647          * Fires before a network request is made to retrieve a data object.
25648          * @param {Object} This DataProxy object.
25649          * @param {Object} params The params parameter to the load function.
25650          */
25651         beforeload : true,
25652         /**
25653          * @event load
25654          * Fires before the load method's callback is called.
25655          * @param {Object} This DataProxy object.
25656          * @param {Object} o The data object.
25657          * @param {Object} arg The callback argument object passed to the load function.
25658          */
25659         load : true,
25660         /**
25661          * @event loadexception
25662          * Fires if an Exception occurs during data retrieval.
25663          * @param {Object} This DataProxy object.
25664          * @param {Object} o The data object.
25665          * @param {Object} arg The callback argument object passed to the load function.
25666          * @param {Object} e The Exception.
25667          */
25668         loadexception : true
25669     });
25670     Roo.data.DataProxy.superclass.constructor.call(this);
25671 };
25672
25673 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25674
25675     /**
25676      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25677      */
25678 /*
25679  * Based on:
25680  * Ext JS Library 1.1.1
25681  * Copyright(c) 2006-2007, Ext JS, LLC.
25682  *
25683  * Originally Released Under LGPL - original licence link has changed is not relivant.
25684  *
25685  * Fork - LGPL
25686  * <script type="text/javascript">
25687  */
25688 /**
25689  * @class Roo.data.MemoryProxy
25690  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25691  * to the Reader when its load method is called.
25692  * @constructor
25693  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25694  */
25695 Roo.data.MemoryProxy = function(data){
25696     if (data.data) {
25697         data = data.data;
25698     }
25699     Roo.data.MemoryProxy.superclass.constructor.call(this);
25700     this.data = data;
25701 };
25702
25703 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25704     
25705     /**
25706      * Load data from the requested source (in this case an in-memory
25707      * data object passed to the constructor), read the data object into
25708      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25709      * process that block using the passed callback.
25710      * @param {Object} params This parameter is not used by the MemoryProxy class.
25711      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25712      * object into a block of Roo.data.Records.
25713      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25714      * The function must be passed <ul>
25715      * <li>The Record block object</li>
25716      * <li>The "arg" argument from the load function</li>
25717      * <li>A boolean success indicator</li>
25718      * </ul>
25719      * @param {Object} scope The scope in which to call the callback
25720      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25721      */
25722     load : function(params, reader, callback, scope, arg){
25723         params = params || {};
25724         var result;
25725         try {
25726             result = reader.readRecords(params.data ? params.data :this.data);
25727         }catch(e){
25728             this.fireEvent("loadexception", this, arg, null, e);
25729             callback.call(scope, null, arg, false);
25730             return;
25731         }
25732         callback.call(scope, result, arg, true);
25733     },
25734     
25735     // private
25736     update : function(params, records){
25737         
25738     }
25739 });/*
25740  * Based on:
25741  * Ext JS Library 1.1.1
25742  * Copyright(c) 2006-2007, Ext JS, LLC.
25743  *
25744  * Originally Released Under LGPL - original licence link has changed is not relivant.
25745  *
25746  * Fork - LGPL
25747  * <script type="text/javascript">
25748  */
25749 /**
25750  * @class Roo.data.HttpProxy
25751  * @extends Roo.data.DataProxy
25752  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25753  * configured to reference a certain URL.<br><br>
25754  * <p>
25755  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25756  * from which the running page was served.<br><br>
25757  * <p>
25758  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25759  * <p>
25760  * Be aware that to enable the browser to parse an XML document, the server must set
25761  * the Content-Type header in the HTTP response to "text/xml".
25762  * @constructor
25763  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25764  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25765  * will be used to make the request.
25766  */
25767 Roo.data.HttpProxy = function(conn){
25768     Roo.data.HttpProxy.superclass.constructor.call(this);
25769     // is conn a conn config or a real conn?
25770     this.conn = conn;
25771     this.useAjax = !conn || !conn.events;
25772   
25773 };
25774
25775 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25776     // thse are take from connection...
25777     
25778     /**
25779      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25780      */
25781     /**
25782      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25783      * extra parameters to each request made by this object. (defaults to undefined)
25784      */
25785     /**
25786      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25787      *  to each request made by this object. (defaults to undefined)
25788      */
25789     /**
25790      * @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)
25791      */
25792     /**
25793      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25794      */
25795      /**
25796      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25797      * @type Boolean
25798      */
25799   
25800
25801     /**
25802      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25803      * @type Boolean
25804      */
25805     /**
25806      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25807      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25808      * a finer-grained basis than the DataProxy events.
25809      */
25810     getConnection : function(){
25811         return this.useAjax ? Roo.Ajax : this.conn;
25812     },
25813
25814     /**
25815      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25816      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25817      * process that block using the passed callback.
25818      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25819      * for the request to the remote server.
25820      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25821      * object into a block of Roo.data.Records.
25822      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25823      * The function must be passed <ul>
25824      * <li>The Record block object</li>
25825      * <li>The "arg" argument from the load function</li>
25826      * <li>A boolean success indicator</li>
25827      * </ul>
25828      * @param {Object} scope The scope in which to call the callback
25829      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25830      */
25831     load : function(params, reader, callback, scope, arg){
25832         if(this.fireEvent("beforeload", this, params) !== false){
25833             var  o = {
25834                 params : params || {},
25835                 request: {
25836                     callback : callback,
25837                     scope : scope,
25838                     arg : arg
25839                 },
25840                 reader: reader,
25841                 callback : this.loadResponse,
25842                 scope: this
25843             };
25844             if(this.useAjax){
25845                 Roo.applyIf(o, this.conn);
25846                 if(this.activeRequest){
25847                     Roo.Ajax.abort(this.activeRequest);
25848                 }
25849                 this.activeRequest = Roo.Ajax.request(o);
25850             }else{
25851                 this.conn.request(o);
25852             }
25853         }else{
25854             callback.call(scope||this, null, arg, false);
25855         }
25856     },
25857
25858     // private
25859     loadResponse : function(o, success, response){
25860         delete this.activeRequest;
25861         if(!success){
25862             this.fireEvent("loadexception", this, o, response);
25863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25864             return;
25865         }
25866         var result;
25867         try {
25868             result = o.reader.read(response);
25869         }catch(e){
25870             o.success = false;
25871             o.raw = { errorMsg : response.responseText };
25872             this.fireEvent("loadexception", this, o, response, e);
25873             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25874             return;
25875         }
25876         
25877         this.fireEvent("load", this, o, o.request.arg);
25878         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25879     },
25880
25881     // private
25882     update : function(dataSet){
25883
25884     },
25885
25886     // private
25887     updateResponse : function(dataSet){
25888
25889     }
25890 });/*
25891  * Based on:
25892  * Ext JS Library 1.1.1
25893  * Copyright(c) 2006-2007, Ext JS, LLC.
25894  *
25895  * Originally Released Under LGPL - original licence link has changed is not relivant.
25896  *
25897  * Fork - LGPL
25898  * <script type="text/javascript">
25899  */
25900
25901 /**
25902  * @class Roo.data.ScriptTagProxy
25903  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25904  * other than the originating domain of the running page.<br><br>
25905  * <p>
25906  * <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
25907  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25908  * <p>
25909  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25910  * source code that is used as the source inside a &lt;script> tag.<br><br>
25911  * <p>
25912  * In order for the browser to process the returned data, the server must wrap the data object
25913  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25914  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25915  * depending on whether the callback name was passed:
25916  * <p>
25917  * <pre><code>
25918 boolean scriptTag = false;
25919 String cb = request.getParameter("callback");
25920 if (cb != null) {
25921     scriptTag = true;
25922     response.setContentType("text/javascript");
25923 } else {
25924     response.setContentType("application/x-json");
25925 }
25926 Writer out = response.getWriter();
25927 if (scriptTag) {
25928     out.write(cb + "(");
25929 }
25930 out.print(dataBlock.toJsonString());
25931 if (scriptTag) {
25932     out.write(");");
25933 }
25934 </pre></code>
25935  *
25936  * @constructor
25937  * @param {Object} config A configuration object.
25938  */
25939 Roo.data.ScriptTagProxy = function(config){
25940     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25941     Roo.apply(this, config);
25942     this.head = document.getElementsByTagName("head")[0];
25943 };
25944
25945 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25946
25947 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25948     /**
25949      * @cfg {String} url The URL from which to request the data object.
25950      */
25951     /**
25952      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25953      */
25954     timeout : 30000,
25955     /**
25956      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25957      * the server the name of the callback function set up by the load call to process the returned data object.
25958      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25959      * javascript output which calls this named function passing the data object as its only parameter.
25960      */
25961     callbackParam : "callback",
25962     /**
25963      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25964      * name to the request.
25965      */
25966     nocache : true,
25967
25968     /**
25969      * Load data from the configured URL, read the data object into
25970      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25971      * process that block using the passed callback.
25972      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25973      * for the request to the remote server.
25974      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25975      * object into a block of Roo.data.Records.
25976      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25977      * The function must be passed <ul>
25978      * <li>The Record block object</li>
25979      * <li>The "arg" argument from the load function</li>
25980      * <li>A boolean success indicator</li>
25981      * </ul>
25982      * @param {Object} scope The scope in which to call the callback
25983      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25984      */
25985     load : function(params, reader, callback, scope, arg){
25986         if(this.fireEvent("beforeload", this, params) !== false){
25987
25988             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25989
25990             var url = this.url;
25991             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25992             if(this.nocache){
25993                 url += "&_dc=" + (new Date().getTime());
25994             }
25995             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25996             var trans = {
25997                 id : transId,
25998                 cb : "stcCallback"+transId,
25999                 scriptId : "stcScript"+transId,
26000                 params : params,
26001                 arg : arg,
26002                 url : url,
26003                 callback : callback,
26004                 scope : scope,
26005                 reader : reader
26006             };
26007             var conn = this;
26008
26009             window[trans.cb] = function(o){
26010                 conn.handleResponse(o, trans);
26011             };
26012
26013             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26014
26015             if(this.autoAbort !== false){
26016                 this.abort();
26017             }
26018
26019             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26020
26021             var script = document.createElement("script");
26022             script.setAttribute("src", url);
26023             script.setAttribute("type", "text/javascript");
26024             script.setAttribute("id", trans.scriptId);
26025             this.head.appendChild(script);
26026
26027             this.trans = trans;
26028         }else{
26029             callback.call(scope||this, null, arg, false);
26030         }
26031     },
26032
26033     // private
26034     isLoading : function(){
26035         return this.trans ? true : false;
26036     },
26037
26038     /**
26039      * Abort the current server request.
26040      */
26041     abort : function(){
26042         if(this.isLoading()){
26043             this.destroyTrans(this.trans);
26044         }
26045     },
26046
26047     // private
26048     destroyTrans : function(trans, isLoaded){
26049         this.head.removeChild(document.getElementById(trans.scriptId));
26050         clearTimeout(trans.timeoutId);
26051         if(isLoaded){
26052             window[trans.cb] = undefined;
26053             try{
26054                 delete window[trans.cb];
26055             }catch(e){}
26056         }else{
26057             // if hasn't been loaded, wait for load to remove it to prevent script error
26058             window[trans.cb] = function(){
26059                 window[trans.cb] = undefined;
26060                 try{
26061                     delete window[trans.cb];
26062                 }catch(e){}
26063             };
26064         }
26065     },
26066
26067     // private
26068     handleResponse : function(o, trans){
26069         this.trans = false;
26070         this.destroyTrans(trans, true);
26071         var result;
26072         try {
26073             result = trans.reader.readRecords(o);
26074         }catch(e){
26075             this.fireEvent("loadexception", this, o, trans.arg, e);
26076             trans.callback.call(trans.scope||window, null, trans.arg, false);
26077             return;
26078         }
26079         this.fireEvent("load", this, o, trans.arg);
26080         trans.callback.call(trans.scope||window, result, trans.arg, true);
26081     },
26082
26083     // private
26084     handleFailure : function(trans){
26085         this.trans = false;
26086         this.destroyTrans(trans, false);
26087         this.fireEvent("loadexception", this, null, trans.arg);
26088         trans.callback.call(trans.scope||window, null, trans.arg, false);
26089     }
26090 });/*
26091  * Based on:
26092  * Ext JS Library 1.1.1
26093  * Copyright(c) 2006-2007, Ext JS, LLC.
26094  *
26095  * Originally Released Under LGPL - original licence link has changed is not relivant.
26096  *
26097  * Fork - LGPL
26098  * <script type="text/javascript">
26099  */
26100
26101 /**
26102  * @class Roo.data.JsonReader
26103  * @extends Roo.data.DataReader
26104  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26105  * based on mappings in a provided Roo.data.Record constructor.
26106  * 
26107  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26108  * in the reply previously. 
26109  * 
26110  * <p>
26111  * Example code:
26112  * <pre><code>
26113 var RecordDef = Roo.data.Record.create([
26114     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26115     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26116 ]);
26117 var myReader = new Roo.data.JsonReader({
26118     totalProperty: "results",    // The property which contains the total dataset size (optional)
26119     root: "rows",                // The property which contains an Array of row objects
26120     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26121 }, RecordDef);
26122 </code></pre>
26123  * <p>
26124  * This would consume a JSON file like this:
26125  * <pre><code>
26126 { 'results': 2, 'rows': [
26127     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26128     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26129 }
26130 </code></pre>
26131  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26132  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26133  * paged from the remote server.
26134  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26135  * @cfg {String} root name of the property which contains the Array of row objects.
26136  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26137  * @cfg {Array} fields Array of field definition objects
26138  * @constructor
26139  * Create a new JsonReader
26140  * @param {Object} meta Metadata configuration options
26141  * @param {Object} recordType Either an Array of field definition objects,
26142  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26143  */
26144 Roo.data.JsonReader = function(meta, recordType){
26145     
26146     meta = meta || {};
26147     // set some defaults:
26148     Roo.applyIf(meta, {
26149         totalProperty: 'total',
26150         successProperty : 'success',
26151         root : 'data',
26152         id : 'id'
26153     });
26154     
26155     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26156 };
26157 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26158     
26159     readerType : 'Json',
26160     
26161     /**
26162      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26163      * Used by Store query builder to append _requestMeta to params.
26164      * 
26165      */
26166     metaFromRemote : false,
26167     /**
26168      * This method is only used by a DataProxy which has retrieved data from a remote server.
26169      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26170      * @return {Object} data A data block which is used by an Roo.data.Store object as
26171      * a cache of Roo.data.Records.
26172      */
26173     read : function(response){
26174         var json = response.responseText;
26175        
26176         var o = /* eval:var:o */ eval("("+json+")");
26177         if(!o) {
26178             throw {message: "JsonReader.read: Json object not found"};
26179         }
26180         
26181         if(o.metaData){
26182             
26183             delete this.ef;
26184             this.metaFromRemote = true;
26185             this.meta = o.metaData;
26186             this.recordType = Roo.data.Record.create(o.metaData.fields);
26187             this.onMetaChange(this.meta, this.recordType, o);
26188         }
26189         return this.readRecords(o);
26190     },
26191
26192     // private function a store will implement
26193     onMetaChange : function(meta, recordType, o){
26194
26195     },
26196
26197     /**
26198          * @ignore
26199          */
26200     simpleAccess: function(obj, subsc) {
26201         return obj[subsc];
26202     },
26203
26204         /**
26205          * @ignore
26206          */
26207     getJsonAccessor: function(){
26208         var re = /[\[\.]/;
26209         return function(expr) {
26210             try {
26211                 return(re.test(expr))
26212                     ? new Function("obj", "return obj." + expr)
26213                     : function(obj){
26214                         return obj[expr];
26215                     };
26216             } catch(e){}
26217             return Roo.emptyFn;
26218         };
26219     }(),
26220
26221     /**
26222      * Create a data block containing Roo.data.Records from an XML document.
26223      * @param {Object} o An object which contains an Array of row objects in the property specified
26224      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26225      * which contains the total size of the dataset.
26226      * @return {Object} data A data block which is used by an Roo.data.Store object as
26227      * a cache of Roo.data.Records.
26228      */
26229     readRecords : function(o){
26230         /**
26231          * After any data loads, the raw JSON data is available for further custom processing.
26232          * @type Object
26233          */
26234         this.o = o;
26235         var s = this.meta, Record = this.recordType,
26236             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26237
26238 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26239         if (!this.ef) {
26240             if(s.totalProperty) {
26241                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26242                 }
26243                 if(s.successProperty) {
26244                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26245                 }
26246                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26247                 if (s.id) {
26248                         var g = this.getJsonAccessor(s.id);
26249                         this.getId = function(rec) {
26250                                 var r = g(rec);  
26251                                 return (r === undefined || r === "") ? null : r;
26252                         };
26253                 } else {
26254                         this.getId = function(){return null;};
26255                 }
26256             this.ef = [];
26257             for(var jj = 0; jj < fl; jj++){
26258                 f = fi[jj];
26259                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26260                 this.ef[jj] = this.getJsonAccessor(map);
26261             }
26262         }
26263
26264         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26265         if(s.totalProperty){
26266             var vt = parseInt(this.getTotal(o), 10);
26267             if(!isNaN(vt)){
26268                 totalRecords = vt;
26269             }
26270         }
26271         if(s.successProperty){
26272             var vs = this.getSuccess(o);
26273             if(vs === false || vs === 'false'){
26274                 success = false;
26275             }
26276         }
26277         var records = [];
26278         for(var i = 0; i < c; i++){
26279             var n = root[i];
26280             var values = {};
26281             var id = this.getId(n);
26282             for(var j = 0; j < fl; j++){
26283                 f = fi[j];
26284                                 var v = this.ef[j](n);
26285                                 if (!f.convert) {
26286                                         Roo.log('missing convert for ' + f.name);
26287                                         Roo.log(f);
26288                                         continue;
26289                                 }
26290                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26291             }
26292                         if (!Record) {
26293                                 return {
26294                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26295                                         success : false,
26296                                         records : [],
26297                                         totalRecords : 0
26298                                 };
26299                         }
26300             var record = new Record(values, id);
26301             record.json = n;
26302             records[i] = record;
26303         }
26304         return {
26305             raw : o,
26306             success : success,
26307             records : records,
26308             totalRecords : totalRecords
26309         };
26310     },
26311     // used when loading children.. @see loadDataFromChildren
26312     toLoadData: function(rec)
26313     {
26314         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26315         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26316         return { data : data, total : data.length };
26317         
26318     }
26319 });/*
26320  * Based on:
26321  * Ext JS Library 1.1.1
26322  * Copyright(c) 2006-2007, Ext JS, LLC.
26323  *
26324  * Originally Released Under LGPL - original licence link has changed is not relivant.
26325  *
26326  * Fork - LGPL
26327  * <script type="text/javascript">
26328  */
26329
26330 /**
26331  * @class Roo.data.XmlReader
26332  * @extends Roo.data.DataReader
26333  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26334  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26335  * <p>
26336  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26337  * header in the HTTP response must be set to "text/xml".</em>
26338  * <p>
26339  * Example code:
26340  * <pre><code>
26341 var RecordDef = Roo.data.Record.create([
26342    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26343    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26344 ]);
26345 var myReader = new Roo.data.XmlReader({
26346    totalRecords: "results", // The element which contains the total dataset size (optional)
26347    record: "row",           // The repeated element which contains row information
26348    id: "id"                 // The element within the row that provides an ID for the record (optional)
26349 }, RecordDef);
26350 </code></pre>
26351  * <p>
26352  * This would consume an XML file like this:
26353  * <pre><code>
26354 &lt;?xml?>
26355 &lt;dataset>
26356  &lt;results>2&lt;/results>
26357  &lt;row>
26358    &lt;id>1&lt;/id>
26359    &lt;name>Bill&lt;/name>
26360    &lt;occupation>Gardener&lt;/occupation>
26361  &lt;/row>
26362  &lt;row>
26363    &lt;id>2&lt;/id>
26364    &lt;name>Ben&lt;/name>
26365    &lt;occupation>Horticulturalist&lt;/occupation>
26366  &lt;/row>
26367 &lt;/dataset>
26368 </code></pre>
26369  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26370  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26371  * paged from the remote server.
26372  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26373  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26374  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26375  * a record identifier value.
26376  * @constructor
26377  * Create a new XmlReader
26378  * @param {Object} meta Metadata configuration options
26379  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26380  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26381  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26382  */
26383 Roo.data.XmlReader = function(meta, recordType){
26384     meta = meta || {};
26385     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26386 };
26387 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26388     
26389     readerType : 'Xml',
26390     
26391     /**
26392      * This method is only used by a DataProxy which has retrieved data from a remote server.
26393          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26394          * to contain a method called 'responseXML' that returns an XML document object.
26395      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26396      * a cache of Roo.data.Records.
26397      */
26398     read : function(response){
26399         var doc = response.responseXML;
26400         if(!doc) {
26401             throw {message: "XmlReader.read: XML Document not available"};
26402         }
26403         return this.readRecords(doc);
26404     },
26405
26406     /**
26407      * Create a data block containing Roo.data.Records from an XML document.
26408          * @param {Object} doc A parsed XML document.
26409      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26410      * a cache of Roo.data.Records.
26411      */
26412     readRecords : function(doc){
26413         /**
26414          * After any data loads/reads, the raw XML Document is available for further custom processing.
26415          * @type XMLDocument
26416          */
26417         this.xmlData = doc;
26418         var root = doc.documentElement || doc;
26419         var q = Roo.DomQuery;
26420         var recordType = this.recordType, fields = recordType.prototype.fields;
26421         var sid = this.meta.id;
26422         var totalRecords = 0, success = true;
26423         if(this.meta.totalRecords){
26424             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26425         }
26426         
26427         if(this.meta.success){
26428             var sv = q.selectValue(this.meta.success, root, true);
26429             success = sv !== false && sv !== 'false';
26430         }
26431         var records = [];
26432         var ns = q.select(this.meta.record, root);
26433         for(var i = 0, len = ns.length; i < len; i++) {
26434                 var n = ns[i];
26435                 var values = {};
26436                 var id = sid ? q.selectValue(sid, n) : undefined;
26437                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26438                     var f = fields.items[j];
26439                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26440                     v = f.convert(v);
26441                     values[f.name] = v;
26442                 }
26443                 var record = new recordType(values, id);
26444                 record.node = n;
26445                 records[records.length] = record;
26446             }
26447
26448             return {
26449                 success : success,
26450                 records : records,
26451                 totalRecords : totalRecords || records.length
26452             };
26453     }
26454 });/*
26455  * Based on:
26456  * Ext JS Library 1.1.1
26457  * Copyright(c) 2006-2007, Ext JS, LLC.
26458  *
26459  * Originally Released Under LGPL - original licence link has changed is not relivant.
26460  *
26461  * Fork - LGPL
26462  * <script type="text/javascript">
26463  */
26464
26465 /**
26466  * @class Roo.data.ArrayReader
26467  * @extends Roo.data.DataReader
26468  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26469  * Each element of that Array represents a row of data fields. The
26470  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26471  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26472  * <p>
26473  * Example code:.
26474  * <pre><code>
26475 var RecordDef = Roo.data.Record.create([
26476     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26477     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26478 ]);
26479 var myReader = new Roo.data.ArrayReader({
26480     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26481 }, RecordDef);
26482 </code></pre>
26483  * <p>
26484  * This would consume an Array like this:
26485  * <pre><code>
26486 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26487   </code></pre>
26488  
26489  * @constructor
26490  * Create a new JsonReader
26491  * @param {Object} meta Metadata configuration options.
26492  * @param {Object|Array} recordType Either an Array of field definition objects
26493  * 
26494  * @cfg {Array} fields Array of field definition objects
26495  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26496  * as specified to {@link Roo.data.Record#create},
26497  * or an {@link Roo.data.Record} object
26498  *
26499  * 
26500  * created using {@link Roo.data.Record#create}.
26501  */
26502 Roo.data.ArrayReader = function(meta, recordType)
26503 {    
26504     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26505 };
26506
26507 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26508     
26509       /**
26510      * Create a data block containing Roo.data.Records from an XML document.
26511      * @param {Object} o An Array of row objects which represents the dataset.
26512      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26513      * a cache of Roo.data.Records.
26514      */
26515     readRecords : function(o)
26516     {
26517         var sid = this.meta ? this.meta.id : null;
26518         var recordType = this.recordType, fields = recordType.prototype.fields;
26519         var records = [];
26520         var root = o;
26521         for(var i = 0; i < root.length; i++){
26522             var n = root[i];
26523             var values = {};
26524             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26525             for(var j = 0, jlen = fields.length; j < jlen; j++){
26526                 var f = fields.items[j];
26527                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26528                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26529                 v = f.convert(v);
26530                 values[f.name] = v;
26531             }
26532             var record = new recordType(values, id);
26533             record.json = n;
26534             records[records.length] = record;
26535         }
26536         return {
26537             records : records,
26538             totalRecords : records.length
26539         };
26540     },
26541     // used when loading children.. @see loadDataFromChildren
26542     toLoadData: function(rec)
26543     {
26544         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26545         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26546         
26547     }
26548     
26549     
26550 });/*
26551  * Based on:
26552  * Ext JS Library 1.1.1
26553  * Copyright(c) 2006-2007, Ext JS, LLC.
26554  *
26555  * Originally Released Under LGPL - original licence link has changed is not relivant.
26556  *
26557  * Fork - LGPL
26558  * <script type="text/javascript">
26559  */
26560
26561
26562 /**
26563  * @class Roo.data.Tree
26564  * @extends Roo.util.Observable
26565  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26566  * in the tree have most standard DOM functionality.
26567  * @constructor
26568  * @param {Node} root (optional) The root node
26569  */
26570 Roo.data.Tree = function(root){
26571    this.nodeHash = {};
26572    /**
26573     * The root node for this tree
26574     * @type Node
26575     */
26576    this.root = null;
26577    if(root){
26578        this.setRootNode(root);
26579    }
26580    this.addEvents({
26581        /**
26582         * @event append
26583         * Fires when a new child node is appended to a node in this tree.
26584         * @param {Tree} tree The owner tree
26585         * @param {Node} parent The parent node
26586         * @param {Node} node The newly appended node
26587         * @param {Number} index The index of the newly appended node
26588         */
26589        "append" : true,
26590        /**
26591         * @event remove
26592         * Fires when a child node is removed from a node in this tree.
26593         * @param {Tree} tree The owner tree
26594         * @param {Node} parent The parent node
26595         * @param {Node} node The child node removed
26596         */
26597        "remove" : true,
26598        /**
26599         * @event move
26600         * Fires when a node is moved to a new location in the tree
26601         * @param {Tree} tree The owner tree
26602         * @param {Node} node The node moved
26603         * @param {Node} oldParent The old parent of this node
26604         * @param {Node} newParent The new parent of this node
26605         * @param {Number} index The index it was moved to
26606         */
26607        "move" : true,
26608        /**
26609         * @event insert
26610         * Fires when a new child node is inserted in a node in this tree.
26611         * @param {Tree} tree The owner tree
26612         * @param {Node} parent The parent node
26613         * @param {Node} node The child node inserted
26614         * @param {Node} refNode The child node the node was inserted before
26615         */
26616        "insert" : true,
26617        /**
26618         * @event beforeappend
26619         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26620         * @param {Tree} tree The owner tree
26621         * @param {Node} parent The parent node
26622         * @param {Node} node The child node to be appended
26623         */
26624        "beforeappend" : true,
26625        /**
26626         * @event beforeremove
26627         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26628         * @param {Tree} tree The owner tree
26629         * @param {Node} parent The parent node
26630         * @param {Node} node The child node to be removed
26631         */
26632        "beforeremove" : true,
26633        /**
26634         * @event beforemove
26635         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26636         * @param {Tree} tree The owner tree
26637         * @param {Node} node The node being moved
26638         * @param {Node} oldParent The parent of the node
26639         * @param {Node} newParent The new parent the node is moving to
26640         * @param {Number} index The index it is being moved to
26641         */
26642        "beforemove" : true,
26643        /**
26644         * @event beforeinsert
26645         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26646         * @param {Tree} tree The owner tree
26647         * @param {Node} parent The parent node
26648         * @param {Node} node The child node to be inserted
26649         * @param {Node} refNode The child node the node is being inserted before
26650         */
26651        "beforeinsert" : true
26652    });
26653
26654     Roo.data.Tree.superclass.constructor.call(this);
26655 };
26656
26657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26658     pathSeparator: "/",
26659
26660     proxyNodeEvent : function(){
26661         return this.fireEvent.apply(this, arguments);
26662     },
26663
26664     /**
26665      * Returns the root node for this tree.
26666      * @return {Node}
26667      */
26668     getRootNode : function(){
26669         return this.root;
26670     },
26671
26672     /**
26673      * Sets the root node for this tree.
26674      * @param {Node} node
26675      * @return {Node}
26676      */
26677     setRootNode : function(node){
26678         this.root = node;
26679         node.ownerTree = this;
26680         node.isRoot = true;
26681         this.registerNode(node);
26682         return node;
26683     },
26684
26685     /**
26686      * Gets a node in this tree by its id.
26687      * @param {String} id
26688      * @return {Node}
26689      */
26690     getNodeById : function(id){
26691         return this.nodeHash[id];
26692     },
26693
26694     registerNode : function(node){
26695         this.nodeHash[node.id] = node;
26696     },
26697
26698     unregisterNode : function(node){
26699         delete this.nodeHash[node.id];
26700     },
26701
26702     toString : function(){
26703         return "[Tree"+(this.id?" "+this.id:"")+"]";
26704     }
26705 });
26706
26707 /**
26708  * @class Roo.data.Node
26709  * @extends Roo.util.Observable
26710  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26711  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26712  * @constructor
26713  * @param {Object} attributes The attributes/config for the node
26714  */
26715 Roo.data.Node = function(attributes){
26716     /**
26717      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26718      * @type {Object}
26719      */
26720     this.attributes = attributes || {};
26721     this.leaf = this.attributes.leaf;
26722     /**
26723      * The node id. @type String
26724      */
26725     this.id = this.attributes.id;
26726     if(!this.id){
26727         this.id = Roo.id(null, "ynode-");
26728         this.attributes.id = this.id;
26729     }
26730      
26731     
26732     /**
26733      * All child nodes of this node. @type Array
26734      */
26735     this.childNodes = [];
26736     if(!this.childNodes.indexOf){ // indexOf is a must
26737         this.childNodes.indexOf = function(o){
26738             for(var i = 0, len = this.length; i < len; i++){
26739                 if(this[i] == o) {
26740                     return i;
26741                 }
26742             }
26743             return -1;
26744         };
26745     }
26746     /**
26747      * The parent node for this node. @type Node
26748      */
26749     this.parentNode = null;
26750     /**
26751      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26752      */
26753     this.firstChild = null;
26754     /**
26755      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26756      */
26757     this.lastChild = null;
26758     /**
26759      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26760      */
26761     this.previousSibling = null;
26762     /**
26763      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26764      */
26765     this.nextSibling = null;
26766
26767     this.addEvents({
26768        /**
26769         * @event append
26770         * Fires when a new child node is appended
26771         * @param {Tree} tree The owner tree
26772         * @param {Node} this This node
26773         * @param {Node} node The newly appended node
26774         * @param {Number} index The index of the newly appended node
26775         */
26776        "append" : true,
26777        /**
26778         * @event remove
26779         * Fires when a child node is removed
26780         * @param {Tree} tree The owner tree
26781         * @param {Node} this This node
26782         * @param {Node} node The removed node
26783         */
26784        "remove" : true,
26785        /**
26786         * @event move
26787         * Fires when this node is moved to a new location in the tree
26788         * @param {Tree} tree The owner tree
26789         * @param {Node} this This node
26790         * @param {Node} oldParent The old parent of this node
26791         * @param {Node} newParent The new parent of this node
26792         * @param {Number} index The index it was moved to
26793         */
26794        "move" : true,
26795        /**
26796         * @event insert
26797         * Fires when a new child node is inserted.
26798         * @param {Tree} tree The owner tree
26799         * @param {Node} this This node
26800         * @param {Node} node The child node inserted
26801         * @param {Node} refNode The child node the node was inserted before
26802         */
26803        "insert" : true,
26804        /**
26805         * @event beforeappend
26806         * Fires before a new child is appended, return false to cancel the append.
26807         * @param {Tree} tree The owner tree
26808         * @param {Node} this This node
26809         * @param {Node} node The child node to be appended
26810         */
26811        "beforeappend" : true,
26812        /**
26813         * @event beforeremove
26814         * Fires before a child is removed, return false to cancel the remove.
26815         * @param {Tree} tree The owner tree
26816         * @param {Node} this This node
26817         * @param {Node} node The child node to be removed
26818         */
26819        "beforeremove" : true,
26820        /**
26821         * @event beforemove
26822         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26823         * @param {Tree} tree The owner tree
26824         * @param {Node} this This node
26825         * @param {Node} oldParent The parent of this node
26826         * @param {Node} newParent The new parent this node is moving to
26827         * @param {Number} index The index it is being moved to
26828         */
26829        "beforemove" : true,
26830        /**
26831         * @event beforeinsert
26832         * Fires before a new child is inserted, return false to cancel the insert.
26833         * @param {Tree} tree The owner tree
26834         * @param {Node} this This node
26835         * @param {Node} node The child node to be inserted
26836         * @param {Node} refNode The child node the node is being inserted before
26837         */
26838        "beforeinsert" : true
26839    });
26840     this.listeners = this.attributes.listeners;
26841     Roo.data.Node.superclass.constructor.call(this);
26842 };
26843
26844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26845     fireEvent : function(evtName){
26846         // first do standard event for this node
26847         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26848             return false;
26849         }
26850         // then bubble it up to the tree if the event wasn't cancelled
26851         var ot = this.getOwnerTree();
26852         if(ot){
26853             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26854                 return false;
26855             }
26856         }
26857         return true;
26858     },
26859
26860     /**
26861      * Returns true if this node is a leaf
26862      * @return {Boolean}
26863      */
26864     isLeaf : function(){
26865         return this.leaf === true;
26866     },
26867
26868     // private
26869     setFirstChild : function(node){
26870         this.firstChild = node;
26871     },
26872
26873     //private
26874     setLastChild : function(node){
26875         this.lastChild = node;
26876     },
26877
26878
26879     /**
26880      * Returns true if this node is the last child of its parent
26881      * @return {Boolean}
26882      */
26883     isLast : function(){
26884        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26885     },
26886
26887     /**
26888      * Returns true if this node is the first child of its parent
26889      * @return {Boolean}
26890      */
26891     isFirst : function(){
26892        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26893     },
26894
26895     hasChildNodes : function(){
26896         return !this.isLeaf() && this.childNodes.length > 0;
26897     },
26898
26899     /**
26900      * Insert node(s) as the last child node of this node.
26901      * @param {Node/Array} node The node or Array of nodes to append
26902      * @return {Node} The appended node if single append, or null if an array was passed
26903      */
26904     appendChild : function(node){
26905         var multi = false;
26906         if(node instanceof Array){
26907             multi = node;
26908         }else if(arguments.length > 1){
26909             multi = arguments;
26910         }
26911         
26912         // if passed an array or multiple args do them one by one
26913         if(multi){
26914             for(var i = 0, len = multi.length; i < len; i++) {
26915                 this.appendChild(multi[i]);
26916             }
26917         }else{
26918             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26919                 return false;
26920             }
26921             var index = this.childNodes.length;
26922             var oldParent = node.parentNode;
26923             // it's a move, make sure we move it cleanly
26924             if(oldParent){
26925                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26926                     return false;
26927                 }
26928                 oldParent.removeChild(node);
26929             }
26930             
26931             index = this.childNodes.length;
26932             if(index == 0){
26933                 this.setFirstChild(node);
26934             }
26935             this.childNodes.push(node);
26936             node.parentNode = this;
26937             var ps = this.childNodes[index-1];
26938             if(ps){
26939                 node.previousSibling = ps;
26940                 ps.nextSibling = node;
26941             }else{
26942                 node.previousSibling = null;
26943             }
26944             node.nextSibling = null;
26945             this.setLastChild(node);
26946             node.setOwnerTree(this.getOwnerTree());
26947             this.fireEvent("append", this.ownerTree, this, node, index);
26948             if(this.ownerTree) {
26949                 this.ownerTree.fireEvent("appendnode", this, node, index);
26950             }
26951             if(oldParent){
26952                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26953             }
26954             return node;
26955         }
26956     },
26957
26958     /**
26959      * Removes a child node from this node.
26960      * @param {Node} node The node to remove
26961      * @return {Node} The removed node
26962      */
26963     removeChild : function(node){
26964         var index = this.childNodes.indexOf(node);
26965         if(index == -1){
26966             return false;
26967         }
26968         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26969             return false;
26970         }
26971
26972         // remove it from childNodes collection
26973         this.childNodes.splice(index, 1);
26974
26975         // update siblings
26976         if(node.previousSibling){
26977             node.previousSibling.nextSibling = node.nextSibling;
26978         }
26979         if(node.nextSibling){
26980             node.nextSibling.previousSibling = node.previousSibling;
26981         }
26982
26983         // update child refs
26984         if(this.firstChild == node){
26985             this.setFirstChild(node.nextSibling);
26986         }
26987         if(this.lastChild == node){
26988             this.setLastChild(node.previousSibling);
26989         }
26990
26991         node.setOwnerTree(null);
26992         // clear any references from the node
26993         node.parentNode = null;
26994         node.previousSibling = null;
26995         node.nextSibling = null;
26996         this.fireEvent("remove", this.ownerTree, this, node);
26997         return node;
26998     },
26999
27000     /**
27001      * Inserts the first node before the second node in this nodes childNodes collection.
27002      * @param {Node} node The node to insert
27003      * @param {Node} refNode The node to insert before (if null the node is appended)
27004      * @return {Node} The inserted node
27005      */
27006     insertBefore : function(node, refNode){
27007         if(!refNode){ // like standard Dom, refNode can be null for append
27008             return this.appendChild(node);
27009         }
27010         // nothing to do
27011         if(node == refNode){
27012             return false;
27013         }
27014
27015         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27016             return false;
27017         }
27018         var index = this.childNodes.indexOf(refNode);
27019         var oldParent = node.parentNode;
27020         var refIndex = index;
27021
27022         // when moving internally, indexes will change after remove
27023         if(oldParent == this && this.childNodes.indexOf(node) < index){
27024             refIndex--;
27025         }
27026
27027         // it's a move, make sure we move it cleanly
27028         if(oldParent){
27029             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27030                 return false;
27031             }
27032             oldParent.removeChild(node);
27033         }
27034         if(refIndex == 0){
27035             this.setFirstChild(node);
27036         }
27037         this.childNodes.splice(refIndex, 0, node);
27038         node.parentNode = this;
27039         var ps = this.childNodes[refIndex-1];
27040         if(ps){
27041             node.previousSibling = ps;
27042             ps.nextSibling = node;
27043         }else{
27044             node.previousSibling = null;
27045         }
27046         node.nextSibling = refNode;
27047         refNode.previousSibling = node;
27048         node.setOwnerTree(this.getOwnerTree());
27049         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27050         if(oldParent){
27051             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27052         }
27053         return node;
27054     },
27055
27056     /**
27057      * Returns the child node at the specified index.
27058      * @param {Number} index
27059      * @return {Node}
27060      */
27061     item : function(index){
27062         return this.childNodes[index];
27063     },
27064
27065     /**
27066      * Replaces one child node in this node with another.
27067      * @param {Node} newChild The replacement node
27068      * @param {Node} oldChild The node to replace
27069      * @return {Node} The replaced node
27070      */
27071     replaceChild : function(newChild, oldChild){
27072         this.insertBefore(newChild, oldChild);
27073         this.removeChild(oldChild);
27074         return oldChild;
27075     },
27076
27077     /**
27078      * Returns the index of a child node
27079      * @param {Node} node
27080      * @return {Number} The index of the node or -1 if it was not found
27081      */
27082     indexOf : function(child){
27083         return this.childNodes.indexOf(child);
27084     },
27085
27086     /**
27087      * Returns the tree this node is in.
27088      * @return {Tree}
27089      */
27090     getOwnerTree : function(){
27091         // if it doesn't have one, look for one
27092         if(!this.ownerTree){
27093             var p = this;
27094             while(p){
27095                 if(p.ownerTree){
27096                     this.ownerTree = p.ownerTree;
27097                     break;
27098                 }
27099                 p = p.parentNode;
27100             }
27101         }
27102         return this.ownerTree;
27103     },
27104
27105     /**
27106      * Returns depth of this node (the root node has a depth of 0)
27107      * @return {Number}
27108      */
27109     getDepth : function(){
27110         var depth = 0;
27111         var p = this;
27112         while(p.parentNode){
27113             ++depth;
27114             p = p.parentNode;
27115         }
27116         return depth;
27117     },
27118
27119     // private
27120     setOwnerTree : function(tree){
27121         // if it's move, we need to update everyone
27122         if(tree != this.ownerTree){
27123             if(this.ownerTree){
27124                 this.ownerTree.unregisterNode(this);
27125             }
27126             this.ownerTree = tree;
27127             var cs = this.childNodes;
27128             for(var i = 0, len = cs.length; i < len; i++) {
27129                 cs[i].setOwnerTree(tree);
27130             }
27131             if(tree){
27132                 tree.registerNode(this);
27133             }
27134         }
27135     },
27136
27137     /**
27138      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27139      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27140      * @return {String} The path
27141      */
27142     getPath : function(attr){
27143         attr = attr || "id";
27144         var p = this.parentNode;
27145         var b = [this.attributes[attr]];
27146         while(p){
27147             b.unshift(p.attributes[attr]);
27148             p = p.parentNode;
27149         }
27150         var sep = this.getOwnerTree().pathSeparator;
27151         return sep + b.join(sep);
27152     },
27153
27154     /**
27155      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27156      * function call will be the scope provided or the current node. The arguments to the function
27157      * will be the args provided or the current node. If the function returns false at any point,
27158      * the bubble is stopped.
27159      * @param {Function} fn The function to call
27160      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27161      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27162      */
27163     bubble : function(fn, scope, args){
27164         var p = this;
27165         while(p){
27166             if(fn.call(scope || p, args || p) === false){
27167                 break;
27168             }
27169             p = p.parentNode;
27170         }
27171     },
27172
27173     /**
27174      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27175      * function call will be the scope provided or the current node. The arguments to the function
27176      * will be the args provided or the current node. If the function returns false at any point,
27177      * the cascade is stopped on that branch.
27178      * @param {Function} fn The function to call
27179      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27180      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27181      */
27182     cascade : function(fn, scope, args){
27183         if(fn.call(scope || this, args || this) !== false){
27184             var cs = this.childNodes;
27185             for(var i = 0, len = cs.length; i < len; i++) {
27186                 cs[i].cascade(fn, scope, args);
27187             }
27188         }
27189     },
27190
27191     /**
27192      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27193      * function call will be the scope provided or the current node. The arguments to the function
27194      * will be the args provided or the current node. If the function returns false at any point,
27195      * the iteration stops.
27196      * @param {Function} fn The function to call
27197      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27198      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27199      */
27200     eachChild : function(fn, scope, args){
27201         var cs = this.childNodes;
27202         for(var i = 0, len = cs.length; i < len; i++) {
27203                 if(fn.call(scope || this, args || cs[i]) === false){
27204                     break;
27205                 }
27206         }
27207     },
27208
27209     /**
27210      * Finds the first child that has the attribute with the specified value.
27211      * @param {String} attribute The attribute name
27212      * @param {Mixed} value The value to search for
27213      * @return {Node} The found child or null if none was found
27214      */
27215     findChild : function(attribute, value){
27216         var cs = this.childNodes;
27217         for(var i = 0, len = cs.length; i < len; i++) {
27218                 if(cs[i].attributes[attribute] == value){
27219                     return cs[i];
27220                 }
27221         }
27222         return null;
27223     },
27224
27225     /**
27226      * Finds the first child by a custom function. The child matches if the function passed
27227      * returns true.
27228      * @param {Function} fn
27229      * @param {Object} scope (optional)
27230      * @return {Node} The found child or null if none was found
27231      */
27232     findChildBy : function(fn, scope){
27233         var cs = this.childNodes;
27234         for(var i = 0, len = cs.length; i < len; i++) {
27235                 if(fn.call(scope||cs[i], cs[i]) === true){
27236                     return cs[i];
27237                 }
27238         }
27239         return null;
27240     },
27241
27242     /**
27243      * Sorts this nodes children using the supplied sort function
27244      * @param {Function} fn
27245      * @param {Object} scope (optional)
27246      */
27247     sort : function(fn, scope){
27248         var cs = this.childNodes;
27249         var len = cs.length;
27250         if(len > 0){
27251             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27252             cs.sort(sortFn);
27253             for(var i = 0; i < len; i++){
27254                 var n = cs[i];
27255                 n.previousSibling = cs[i-1];
27256                 n.nextSibling = cs[i+1];
27257                 if(i == 0){
27258                     this.setFirstChild(n);
27259                 }
27260                 if(i == len-1){
27261                     this.setLastChild(n);
27262                 }
27263             }
27264         }
27265     },
27266
27267     /**
27268      * Returns true if this node is an ancestor (at any point) of the passed node.
27269      * @param {Node} node
27270      * @return {Boolean}
27271      */
27272     contains : function(node){
27273         return node.isAncestor(this);
27274     },
27275
27276     /**
27277      * Returns true if the passed node is an ancestor (at any point) of this node.
27278      * @param {Node} node
27279      * @return {Boolean}
27280      */
27281     isAncestor : function(node){
27282         var p = this.parentNode;
27283         while(p){
27284             if(p == node){
27285                 return true;
27286             }
27287             p = p.parentNode;
27288         }
27289         return false;
27290     },
27291
27292     toString : function(){
27293         return "[Node"+(this.id?" "+this.id:"")+"]";
27294     }
27295 });/*
27296  * Based on:
27297  * Ext JS Library 1.1.1
27298  * Copyright(c) 2006-2007, Ext JS, LLC.
27299  *
27300  * Originally Released Under LGPL - original licence link has changed is not relivant.
27301  *
27302  * Fork - LGPL
27303  * <script type="text/javascript">
27304  */
27305
27306
27307 /**
27308  * @class Roo.Shadow
27309  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27310  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27311  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27312  * @constructor
27313  * Create a new Shadow
27314  * @param {Object} config The config object
27315  */
27316 Roo.Shadow = function(config){
27317     Roo.apply(this, config);
27318     if(typeof this.mode != "string"){
27319         this.mode = this.defaultMode;
27320     }
27321     var o = this.offset, a = {h: 0};
27322     var rad = Math.floor(this.offset/2);
27323     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27324         case "drop":
27325             a.w = 0;
27326             a.l = a.t = o;
27327             a.t -= 1;
27328             if(Roo.isIE){
27329                 a.l -= this.offset + rad;
27330                 a.t -= this.offset + rad;
27331                 a.w -= rad;
27332                 a.h -= rad;
27333                 a.t += 1;
27334             }
27335         break;
27336         case "sides":
27337             a.w = (o*2);
27338             a.l = -o;
27339             a.t = o-1;
27340             if(Roo.isIE){
27341                 a.l -= (this.offset - rad);
27342                 a.t -= this.offset + rad;
27343                 a.l += 1;
27344                 a.w -= (this.offset - rad)*2;
27345                 a.w -= rad + 1;
27346                 a.h -= 1;
27347             }
27348         break;
27349         case "frame":
27350             a.w = a.h = (o*2);
27351             a.l = a.t = -o;
27352             a.t += 1;
27353             a.h -= 2;
27354             if(Roo.isIE){
27355                 a.l -= (this.offset - rad);
27356                 a.t -= (this.offset - rad);
27357                 a.l += 1;
27358                 a.w -= (this.offset + rad + 1);
27359                 a.h -= (this.offset + rad);
27360                 a.h += 1;
27361             }
27362         break;
27363     };
27364
27365     this.adjusts = a;
27366 };
27367
27368 Roo.Shadow.prototype = {
27369     /**
27370      * @cfg {String} mode
27371      * The shadow display mode.  Supports the following options:<br />
27372      * sides: Shadow displays on both sides and bottom only<br />
27373      * frame: Shadow displays equally on all four sides<br />
27374      * drop: Traditional bottom-right drop shadow (default)
27375      */
27376     mode: false,
27377     /**
27378      * @cfg {String} offset
27379      * The number of pixels to offset the shadow from the element (defaults to 4)
27380      */
27381     offset: 4,
27382
27383     // private
27384     defaultMode: "drop",
27385
27386     /**
27387      * Displays the shadow under the target element
27388      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27389      */
27390     show : function(target){
27391         target = Roo.get(target);
27392         if(!this.el){
27393             this.el = Roo.Shadow.Pool.pull();
27394             if(this.el.dom.nextSibling != target.dom){
27395                 this.el.insertBefore(target);
27396             }
27397         }
27398         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27399         if(Roo.isIE){
27400             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27401         }
27402         this.realign(
27403             target.getLeft(true),
27404             target.getTop(true),
27405             target.getWidth(),
27406             target.getHeight()
27407         );
27408         this.el.dom.style.display = "block";
27409     },
27410
27411     /**
27412      * Returns true if the shadow is visible, else false
27413      */
27414     isVisible : function(){
27415         return this.el ? true : false;  
27416     },
27417
27418     /**
27419      * Direct alignment when values are already available. Show must be called at least once before
27420      * calling this method to ensure it is initialized.
27421      * @param {Number} left The target element left position
27422      * @param {Number} top The target element top position
27423      * @param {Number} width The target element width
27424      * @param {Number} height The target element height
27425      */
27426     realign : function(l, t, w, h){
27427         if(!this.el){
27428             return;
27429         }
27430         var a = this.adjusts, d = this.el.dom, s = d.style;
27431         var iea = 0;
27432         s.left = (l+a.l)+"px";
27433         s.top = (t+a.t)+"px";
27434         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27435  
27436         if(s.width != sws || s.height != shs){
27437             s.width = sws;
27438             s.height = shs;
27439             if(!Roo.isIE){
27440                 var cn = d.childNodes;
27441                 var sww = Math.max(0, (sw-12))+"px";
27442                 cn[0].childNodes[1].style.width = sww;
27443                 cn[1].childNodes[1].style.width = sww;
27444                 cn[2].childNodes[1].style.width = sww;
27445                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27446             }
27447         }
27448     },
27449
27450     /**
27451      * Hides this shadow
27452      */
27453     hide : function(){
27454         if(this.el){
27455             this.el.dom.style.display = "none";
27456             Roo.Shadow.Pool.push(this.el);
27457             delete this.el;
27458         }
27459     },
27460
27461     /**
27462      * Adjust the z-index of this shadow
27463      * @param {Number} zindex The new z-index
27464      */
27465     setZIndex : function(z){
27466         this.zIndex = z;
27467         if(this.el){
27468             this.el.setStyle("z-index", z);
27469         }
27470     }
27471 };
27472
27473 // Private utility class that manages the internal Shadow cache
27474 Roo.Shadow.Pool = function(){
27475     var p = [];
27476     var markup = Roo.isIE ?
27477                  '<div class="x-ie-shadow"></div>' :
27478                  '<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>';
27479     return {
27480         pull : function(){
27481             var sh = p.shift();
27482             if(!sh){
27483                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27484                 sh.autoBoxAdjust = false;
27485             }
27486             return sh;
27487         },
27488
27489         push : function(sh){
27490             p.push(sh);
27491         }
27492     };
27493 }();/*
27494  * Based on:
27495  * Ext JS Library 1.1.1
27496  * Copyright(c) 2006-2007, Ext JS, LLC.
27497  *
27498  * Originally Released Under LGPL - original licence link has changed is not relivant.
27499  *
27500  * Fork - LGPL
27501  * <script type="text/javascript">
27502  */
27503
27504
27505 /**
27506  * @class Roo.SplitBar
27507  * @extends Roo.util.Observable
27508  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27509  * <br><br>
27510  * Usage:
27511  * <pre><code>
27512 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27513                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27514 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27515 split.minSize = 100;
27516 split.maxSize = 600;
27517 split.animate = true;
27518 split.on('moved', splitterMoved);
27519 </code></pre>
27520  * @constructor
27521  * Create a new SplitBar
27522  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27523  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27524  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27525  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27526                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27527                         position of the SplitBar).
27528  */
27529 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27530     
27531     /** @private */
27532     this.el = Roo.get(dragElement, true);
27533     this.el.dom.unselectable = "on";
27534     /** @private */
27535     this.resizingEl = Roo.get(resizingElement, true);
27536
27537     /**
27538      * @private
27539      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27540      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27541      * @type Number
27542      */
27543     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27544     
27545     /**
27546      * The minimum size of the resizing element. (Defaults to 0)
27547      * @type Number
27548      */
27549     this.minSize = 0;
27550     
27551     /**
27552      * The maximum size of the resizing element. (Defaults to 2000)
27553      * @type Number
27554      */
27555     this.maxSize = 2000;
27556     
27557     /**
27558      * Whether to animate the transition to the new size
27559      * @type Boolean
27560      */
27561     this.animate = false;
27562     
27563     /**
27564      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27565      * @type Boolean
27566      */
27567     this.useShim = false;
27568     
27569     /** @private */
27570     this.shim = null;
27571     
27572     if(!existingProxy){
27573         /** @private */
27574         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27575     }else{
27576         this.proxy = Roo.get(existingProxy).dom;
27577     }
27578     /** @private */
27579     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27580     
27581     /** @private */
27582     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27583     
27584     /** @private */
27585     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27586     
27587     /** @private */
27588     this.dragSpecs = {};
27589     
27590     /**
27591      * @private The adapter to use to positon and resize elements
27592      */
27593     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27594     this.adapter.init(this);
27595     
27596     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27597         /** @private */
27598         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27599         this.el.addClass("x-splitbar-h");
27600     }else{
27601         /** @private */
27602         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27603         this.el.addClass("x-splitbar-v");
27604     }
27605     
27606     this.addEvents({
27607         /**
27608          * @event resize
27609          * Fires when the splitter is moved (alias for {@link #event-moved})
27610          * @param {Roo.SplitBar} this
27611          * @param {Number} newSize the new width or height
27612          */
27613         "resize" : true,
27614         /**
27615          * @event moved
27616          * Fires when the splitter is moved
27617          * @param {Roo.SplitBar} this
27618          * @param {Number} newSize the new width or height
27619          */
27620         "moved" : true,
27621         /**
27622          * @event beforeresize
27623          * Fires before the splitter is dragged
27624          * @param {Roo.SplitBar} this
27625          */
27626         "beforeresize" : true,
27627
27628         "beforeapply" : true
27629     });
27630
27631     Roo.util.Observable.call(this);
27632 };
27633
27634 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27635     onStartProxyDrag : function(x, y){
27636         this.fireEvent("beforeresize", this);
27637         if(!this.overlay){
27638             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27639             o.unselectable();
27640             o.enableDisplayMode("block");
27641             // all splitbars share the same overlay
27642             Roo.SplitBar.prototype.overlay = o;
27643         }
27644         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27645         this.overlay.show();
27646         Roo.get(this.proxy).setDisplayed("block");
27647         var size = this.adapter.getElementSize(this);
27648         this.activeMinSize = this.getMinimumSize();;
27649         this.activeMaxSize = this.getMaximumSize();;
27650         var c1 = size - this.activeMinSize;
27651         var c2 = Math.max(this.activeMaxSize - size, 0);
27652         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27653             this.dd.resetConstraints();
27654             this.dd.setXConstraint(
27655                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27656                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27657             );
27658             this.dd.setYConstraint(0, 0);
27659         }else{
27660             this.dd.resetConstraints();
27661             this.dd.setXConstraint(0, 0);
27662             this.dd.setYConstraint(
27663                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27664                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27665             );
27666          }
27667         this.dragSpecs.startSize = size;
27668         this.dragSpecs.startPoint = [x, y];
27669         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27670     },
27671     
27672     /** 
27673      * @private Called after the drag operation by the DDProxy
27674      */
27675     onEndProxyDrag : function(e){
27676         Roo.get(this.proxy).setDisplayed(false);
27677         var endPoint = Roo.lib.Event.getXY(e);
27678         if(this.overlay){
27679             this.overlay.hide();
27680         }
27681         var newSize;
27682         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27683             newSize = this.dragSpecs.startSize + 
27684                 (this.placement == Roo.SplitBar.LEFT ?
27685                     endPoint[0] - this.dragSpecs.startPoint[0] :
27686                     this.dragSpecs.startPoint[0] - endPoint[0]
27687                 );
27688         }else{
27689             newSize = this.dragSpecs.startSize + 
27690                 (this.placement == Roo.SplitBar.TOP ?
27691                     endPoint[1] - this.dragSpecs.startPoint[1] :
27692                     this.dragSpecs.startPoint[1] - endPoint[1]
27693                 );
27694         }
27695         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27696         if(newSize != this.dragSpecs.startSize){
27697             if(this.fireEvent('beforeapply', this, newSize) !== false){
27698                 this.adapter.setElementSize(this, newSize);
27699                 this.fireEvent("moved", this, newSize);
27700                 this.fireEvent("resize", this, newSize);
27701             }
27702         }
27703     },
27704     
27705     /**
27706      * Get the adapter this SplitBar uses
27707      * @return The adapter object
27708      */
27709     getAdapter : function(){
27710         return this.adapter;
27711     },
27712     
27713     /**
27714      * Set the adapter this SplitBar uses
27715      * @param {Object} adapter A SplitBar adapter object
27716      */
27717     setAdapter : function(adapter){
27718         this.adapter = adapter;
27719         this.adapter.init(this);
27720     },
27721     
27722     /**
27723      * Gets the minimum size for the resizing element
27724      * @return {Number} The minimum size
27725      */
27726     getMinimumSize : function(){
27727         return this.minSize;
27728     },
27729     
27730     /**
27731      * Sets the minimum size for the resizing element
27732      * @param {Number} minSize The minimum size
27733      */
27734     setMinimumSize : function(minSize){
27735         this.minSize = minSize;
27736     },
27737     
27738     /**
27739      * Gets the maximum size for the resizing element
27740      * @return {Number} The maximum size
27741      */
27742     getMaximumSize : function(){
27743         return this.maxSize;
27744     },
27745     
27746     /**
27747      * Sets the maximum size for the resizing element
27748      * @param {Number} maxSize The maximum size
27749      */
27750     setMaximumSize : function(maxSize){
27751         this.maxSize = maxSize;
27752     },
27753     
27754     /**
27755      * Sets the initialize size for the resizing element
27756      * @param {Number} size The initial size
27757      */
27758     setCurrentSize : function(size){
27759         var oldAnimate = this.animate;
27760         this.animate = false;
27761         this.adapter.setElementSize(this, size);
27762         this.animate = oldAnimate;
27763     },
27764     
27765     /**
27766      * Destroy this splitbar. 
27767      * @param {Boolean} removeEl True to remove the element
27768      */
27769     destroy : function(removeEl){
27770         if(this.shim){
27771             this.shim.remove();
27772         }
27773         this.dd.unreg();
27774         this.proxy.parentNode.removeChild(this.proxy);
27775         if(removeEl){
27776             this.el.remove();
27777         }
27778     }
27779 });
27780
27781 /**
27782  * @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.
27783  */
27784 Roo.SplitBar.createProxy = function(dir){
27785     var proxy = new Roo.Element(document.createElement("div"));
27786     proxy.unselectable();
27787     var cls = 'x-splitbar-proxy';
27788     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27789     document.body.appendChild(proxy.dom);
27790     return proxy.dom;
27791 };
27792
27793 /** 
27794  * @class Roo.SplitBar.BasicLayoutAdapter
27795  * Default Adapter. It assumes the splitter and resizing element are not positioned
27796  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27797  */
27798 Roo.SplitBar.BasicLayoutAdapter = function(){
27799 };
27800
27801 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27802     // do nothing for now
27803     init : function(s){
27804     
27805     },
27806     /**
27807      * Called before drag operations to get the current size of the resizing element. 
27808      * @param {Roo.SplitBar} s The SplitBar using this adapter
27809      */
27810      getElementSize : function(s){
27811         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27812             return s.resizingEl.getWidth();
27813         }else{
27814             return s.resizingEl.getHeight();
27815         }
27816     },
27817     
27818     /**
27819      * Called after drag operations to set the size of the resizing element.
27820      * @param {Roo.SplitBar} s The SplitBar using this adapter
27821      * @param {Number} newSize The new size to set
27822      * @param {Function} onComplete A function to be invoked when resizing is complete
27823      */
27824     setElementSize : function(s, newSize, onComplete){
27825         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27826             if(!s.animate){
27827                 s.resizingEl.setWidth(newSize);
27828                 if(onComplete){
27829                     onComplete(s, newSize);
27830                 }
27831             }else{
27832                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27833             }
27834         }else{
27835             
27836             if(!s.animate){
27837                 s.resizingEl.setHeight(newSize);
27838                 if(onComplete){
27839                     onComplete(s, newSize);
27840                 }
27841             }else{
27842                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27843             }
27844         }
27845     }
27846 };
27847
27848 /** 
27849  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27850  * @extends Roo.SplitBar.BasicLayoutAdapter
27851  * Adapter that  moves the splitter element to align with the resized sizing element. 
27852  * Used with an absolute positioned SplitBar.
27853  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27854  * document.body, make sure you assign an id to the body element.
27855  */
27856 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27857     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27858     this.container = Roo.get(container);
27859 };
27860
27861 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27862     init : function(s){
27863         this.basic.init(s);
27864     },
27865     
27866     getElementSize : function(s){
27867         return this.basic.getElementSize(s);
27868     },
27869     
27870     setElementSize : function(s, newSize, onComplete){
27871         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27872     },
27873     
27874     moveSplitter : function(s){
27875         var yes = Roo.SplitBar;
27876         switch(s.placement){
27877             case yes.LEFT:
27878                 s.el.setX(s.resizingEl.getRight());
27879                 break;
27880             case yes.RIGHT:
27881                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27882                 break;
27883             case yes.TOP:
27884                 s.el.setY(s.resizingEl.getBottom());
27885                 break;
27886             case yes.BOTTOM:
27887                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27888                 break;
27889         }
27890     }
27891 };
27892
27893 /**
27894  * Orientation constant - Create a vertical SplitBar
27895  * @static
27896  * @type Number
27897  */
27898 Roo.SplitBar.VERTICAL = 1;
27899
27900 /**
27901  * Orientation constant - Create a horizontal SplitBar
27902  * @static
27903  * @type Number
27904  */
27905 Roo.SplitBar.HORIZONTAL = 2;
27906
27907 /**
27908  * Placement constant - The resizing element is to the left of the splitter element
27909  * @static
27910  * @type Number
27911  */
27912 Roo.SplitBar.LEFT = 1;
27913
27914 /**
27915  * Placement constant - The resizing element is to the right of the splitter element
27916  * @static
27917  * @type Number
27918  */
27919 Roo.SplitBar.RIGHT = 2;
27920
27921 /**
27922  * Placement constant - The resizing element is positioned above the splitter element
27923  * @static
27924  * @type Number
27925  */
27926 Roo.SplitBar.TOP = 3;
27927
27928 /**
27929  * Placement constant - The resizing element is positioned under splitter element
27930  * @static
27931  * @type Number
27932  */
27933 Roo.SplitBar.BOTTOM = 4;
27934 /*
27935  * Based on:
27936  * Ext JS Library 1.1.1
27937  * Copyright(c) 2006-2007, Ext JS, LLC.
27938  *
27939  * Originally Released Under LGPL - original licence link has changed is not relivant.
27940  *
27941  * Fork - LGPL
27942  * <script type="text/javascript">
27943  */
27944
27945 /**
27946  * @class Roo.View
27947  * @extends Roo.util.Observable
27948  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27949  * This class also supports single and multi selection modes. <br>
27950  * Create a data model bound view:
27951  <pre><code>
27952  var store = new Roo.data.Store(...);
27953
27954  var view = new Roo.View({
27955     el : "my-element",
27956     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27957  
27958     singleSelect: true,
27959     selectedClass: "ydataview-selected",
27960     store: store
27961  });
27962
27963  // listen for node click?
27964  view.on("click", function(vw, index, node, e){
27965  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27966  });
27967
27968  // load XML data
27969  dataModel.load("foobar.xml");
27970  </code></pre>
27971  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27972  * <br><br>
27973  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27974  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27975  * 
27976  * Note: old style constructor is still suported (container, template, config)
27977  * 
27978  * @constructor
27979  * Create a new View
27980  * @param {Object} config The config object
27981  * 
27982  */
27983 Roo.View = function(config, depreciated_tpl, depreciated_config){
27984     
27985     this.parent = false;
27986     
27987     if (typeof(depreciated_tpl) == 'undefined') {
27988         // new way.. - universal constructor.
27989         Roo.apply(this, config);
27990         this.el  = Roo.get(this.el);
27991     } else {
27992         // old format..
27993         this.el  = Roo.get(config);
27994         this.tpl = depreciated_tpl;
27995         Roo.apply(this, depreciated_config);
27996     }
27997     this.wrapEl  = this.el.wrap().wrap();
27998     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27999     
28000     
28001     if(typeof(this.tpl) == "string"){
28002         this.tpl = new Roo.Template(this.tpl);
28003     } else {
28004         // support xtype ctors..
28005         this.tpl = new Roo.factory(this.tpl, Roo);
28006     }
28007     
28008     
28009     this.tpl.compile();
28010     
28011     /** @private */
28012     this.addEvents({
28013         /**
28014          * @event beforeclick
28015          * Fires before a click is processed. Returns false to cancel the default action.
28016          * @param {Roo.View} this
28017          * @param {Number} index The index of the target node
28018          * @param {HTMLElement} node The target node
28019          * @param {Roo.EventObject} e The raw event object
28020          */
28021             "beforeclick" : true,
28022         /**
28023          * @event click
28024          * Fires when a template node is clicked.
28025          * @param {Roo.View} this
28026          * @param {Number} index The index of the target node
28027          * @param {HTMLElement} node The target node
28028          * @param {Roo.EventObject} e The raw event object
28029          */
28030             "click" : true,
28031         /**
28032          * @event dblclick
28033          * Fires when a template node is double clicked.
28034          * @param {Roo.View} this
28035          * @param {Number} index The index of the target node
28036          * @param {HTMLElement} node The target node
28037          * @param {Roo.EventObject} e The raw event object
28038          */
28039             "dblclick" : true,
28040         /**
28041          * @event contextmenu
28042          * Fires when a template node is right clicked.
28043          * @param {Roo.View} this
28044          * @param {Number} index The index of the target node
28045          * @param {HTMLElement} node The target node
28046          * @param {Roo.EventObject} e The raw event object
28047          */
28048             "contextmenu" : true,
28049         /**
28050          * @event selectionchange
28051          * Fires when the selected nodes change.
28052          * @param {Roo.View} this
28053          * @param {Array} selections Array of the selected nodes
28054          */
28055             "selectionchange" : true,
28056     
28057         /**
28058          * @event beforeselect
28059          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28060          * @param {Roo.View} this
28061          * @param {HTMLElement} node The node to be selected
28062          * @param {Array} selections Array of currently selected nodes
28063          */
28064             "beforeselect" : true,
28065         /**
28066          * @event preparedata
28067          * Fires on every row to render, to allow you to change the data.
28068          * @param {Roo.View} this
28069          * @param {Object} data to be rendered (change this)
28070          */
28071           "preparedata" : true
28072           
28073           
28074         });
28075
28076
28077
28078     this.el.on({
28079         "click": this.onClick,
28080         "dblclick": this.onDblClick,
28081         "contextmenu": this.onContextMenu,
28082         scope:this
28083     });
28084
28085     this.selections = [];
28086     this.nodes = [];
28087     this.cmp = new Roo.CompositeElementLite([]);
28088     if(this.store){
28089         this.store = Roo.factory(this.store, Roo.data);
28090         this.setStore(this.store, true);
28091     }
28092     
28093     if ( this.footer && this.footer.xtype) {
28094            
28095          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28096         
28097         this.footer.dataSource = this.store;
28098         this.footer.container = fctr;
28099         this.footer = Roo.factory(this.footer, Roo);
28100         fctr.insertFirst(this.el);
28101         
28102         // this is a bit insane - as the paging toolbar seems to detach the el..
28103 //        dom.parentNode.parentNode.parentNode
28104          // they get detached?
28105     }
28106     
28107     
28108     Roo.View.superclass.constructor.call(this);
28109     
28110     
28111 };
28112
28113 Roo.extend(Roo.View, Roo.util.Observable, {
28114     
28115      /**
28116      * @cfg {Roo.data.Store} store Data store to load data from.
28117      */
28118     store : false,
28119     
28120     /**
28121      * @cfg {String|Roo.Element} el The container element.
28122      */
28123     el : '',
28124     
28125     /**
28126      * @cfg {String|Roo.Template} tpl The template used by this View 
28127      */
28128     tpl : false,
28129     /**
28130      * @cfg {String} dataName the named area of the template to use as the data area
28131      *                          Works with domtemplates roo-name="name"
28132      */
28133     dataName: false,
28134     /**
28135      * @cfg {String} selectedClass The css class to add to selected nodes
28136      */
28137     selectedClass : "x-view-selected",
28138      /**
28139      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28140      */
28141     emptyText : "",
28142     
28143     /**
28144      * @cfg {String} text to display on mask (default Loading)
28145      */
28146     mask : false,
28147     /**
28148      * @cfg {Boolean} multiSelect Allow multiple selection
28149      */
28150     multiSelect : false,
28151     /**
28152      * @cfg {Boolean} singleSelect Allow single selection
28153      */
28154     singleSelect:  false,
28155     
28156     /**
28157      * @cfg {Boolean} toggleSelect - selecting 
28158      */
28159     toggleSelect : false,
28160     
28161     /**
28162      * @cfg {Boolean} tickable - selecting 
28163      */
28164     tickable : false,
28165     
28166     /**
28167      * Returns the element this view is bound to.
28168      * @return {Roo.Element}
28169      */
28170     getEl : function(){
28171         return this.wrapEl;
28172     },
28173     
28174     
28175
28176     /**
28177      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28178      */
28179     refresh : function(){
28180         //Roo.log('refresh');
28181         var t = this.tpl;
28182         
28183         // if we are using something like 'domtemplate', then
28184         // the what gets used is:
28185         // t.applySubtemplate(NAME, data, wrapping data..)
28186         // the outer template then get' applied with
28187         //     the store 'extra data'
28188         // and the body get's added to the
28189         //      roo-name="data" node?
28190         //      <span class='roo-tpl-{name}'></span> ?????
28191         
28192         
28193         
28194         this.clearSelections();
28195         this.el.update("");
28196         var html = [];
28197         var records = this.store.getRange();
28198         if(records.length < 1) {
28199             
28200             // is this valid??  = should it render a template??
28201             
28202             this.el.update(this.emptyText);
28203             return;
28204         }
28205         var el = this.el;
28206         if (this.dataName) {
28207             this.el.update(t.apply(this.store.meta)); //????
28208             el = this.el.child('.roo-tpl-' + this.dataName);
28209         }
28210         
28211         for(var i = 0, len = records.length; i < len; i++){
28212             var data = this.prepareData(records[i].data, i, records[i]);
28213             this.fireEvent("preparedata", this, data, i, records[i]);
28214             
28215             var d = Roo.apply({}, data);
28216             
28217             if(this.tickable){
28218                 Roo.apply(d, {'roo-id' : Roo.id()});
28219                 
28220                 var _this = this;
28221             
28222                 Roo.each(this.parent.item, function(item){
28223                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28224                         return;
28225                     }
28226                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28227                 });
28228             }
28229             
28230             html[html.length] = Roo.util.Format.trim(
28231                 this.dataName ?
28232                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28233                     t.apply(d)
28234             );
28235         }
28236         
28237         
28238         
28239         el.update(html.join(""));
28240         this.nodes = el.dom.childNodes;
28241         this.updateIndexes(0);
28242     },
28243     
28244
28245     /**
28246      * Function to override to reformat the data that is sent to
28247      * the template for each node.
28248      * DEPRICATED - use the preparedata event handler.
28249      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28250      * a JSON object for an UpdateManager bound view).
28251      */
28252     prepareData : function(data, index, record)
28253     {
28254         this.fireEvent("preparedata", this, data, index, record);
28255         return data;
28256     },
28257
28258     onUpdate : function(ds, record){
28259         // Roo.log('on update');   
28260         this.clearSelections();
28261         var index = this.store.indexOf(record);
28262         var n = this.nodes[index];
28263         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28264         n.parentNode.removeChild(n);
28265         this.updateIndexes(index, index);
28266     },
28267
28268     
28269     
28270 // --------- FIXME     
28271     onAdd : function(ds, records, index)
28272     {
28273         //Roo.log(['on Add', ds, records, index] );        
28274         this.clearSelections();
28275         if(this.nodes.length == 0){
28276             this.refresh();
28277             return;
28278         }
28279         var n = this.nodes[index];
28280         for(var i = 0, len = records.length; i < len; i++){
28281             var d = this.prepareData(records[i].data, i, records[i]);
28282             if(n){
28283                 this.tpl.insertBefore(n, d);
28284             }else{
28285                 
28286                 this.tpl.append(this.el, d);
28287             }
28288         }
28289         this.updateIndexes(index);
28290     },
28291
28292     onRemove : function(ds, record, index){
28293        // Roo.log('onRemove');
28294         this.clearSelections();
28295         var el = this.dataName  ?
28296             this.el.child('.roo-tpl-' + this.dataName) :
28297             this.el; 
28298         
28299         el.dom.removeChild(this.nodes[index]);
28300         this.updateIndexes(index);
28301     },
28302
28303     /**
28304      * Refresh an individual node.
28305      * @param {Number} index
28306      */
28307     refreshNode : function(index){
28308         this.onUpdate(this.store, this.store.getAt(index));
28309     },
28310
28311     updateIndexes : function(startIndex, endIndex){
28312         var ns = this.nodes;
28313         startIndex = startIndex || 0;
28314         endIndex = endIndex || ns.length - 1;
28315         for(var i = startIndex; i <= endIndex; i++){
28316             ns[i].nodeIndex = i;
28317         }
28318     },
28319
28320     /**
28321      * Changes the data store this view uses and refresh the view.
28322      * @param {Store} store
28323      */
28324     setStore : function(store, initial){
28325         if(!initial && this.store){
28326             this.store.un("datachanged", this.refresh);
28327             this.store.un("add", this.onAdd);
28328             this.store.un("remove", this.onRemove);
28329             this.store.un("update", this.onUpdate);
28330             this.store.un("clear", this.refresh);
28331             this.store.un("beforeload", this.onBeforeLoad);
28332             this.store.un("load", this.onLoad);
28333             this.store.un("loadexception", this.onLoad);
28334         }
28335         if(store){
28336           
28337             store.on("datachanged", this.refresh, this);
28338             store.on("add", this.onAdd, this);
28339             store.on("remove", this.onRemove, this);
28340             store.on("update", this.onUpdate, this);
28341             store.on("clear", this.refresh, this);
28342             store.on("beforeload", this.onBeforeLoad, this);
28343             store.on("load", this.onLoad, this);
28344             store.on("loadexception", this.onLoad, this);
28345         }
28346         
28347         if(store){
28348             this.refresh();
28349         }
28350     },
28351     /**
28352      * onbeforeLoad - masks the loading area.
28353      *
28354      */
28355     onBeforeLoad : function(store,opts)
28356     {
28357          //Roo.log('onBeforeLoad');   
28358         if (!opts.add) {
28359             this.el.update("");
28360         }
28361         this.el.mask(this.mask ? this.mask : "Loading" ); 
28362     },
28363     onLoad : function ()
28364     {
28365         this.el.unmask();
28366     },
28367     
28368
28369     /**
28370      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28371      * @param {HTMLElement} node
28372      * @return {HTMLElement} The template node
28373      */
28374     findItemFromChild : function(node){
28375         var el = this.dataName  ?
28376             this.el.child('.roo-tpl-' + this.dataName,true) :
28377             this.el.dom; 
28378         
28379         if(!node || node.parentNode == el){
28380                     return node;
28381             }
28382             var p = node.parentNode;
28383             while(p && p != el){
28384             if(p.parentNode == el){
28385                 return p;
28386             }
28387             p = p.parentNode;
28388         }
28389             return null;
28390     },
28391
28392     /** @ignore */
28393     onClick : function(e){
28394         var item = this.findItemFromChild(e.getTarget());
28395         if(item){
28396             var index = this.indexOf(item);
28397             if(this.onItemClick(item, index, e) !== false){
28398                 this.fireEvent("click", this, index, item, e);
28399             }
28400         }else{
28401             this.clearSelections();
28402         }
28403     },
28404
28405     /** @ignore */
28406     onContextMenu : function(e){
28407         var item = this.findItemFromChild(e.getTarget());
28408         if(item){
28409             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28410         }
28411     },
28412
28413     /** @ignore */
28414     onDblClick : function(e){
28415         var item = this.findItemFromChild(e.getTarget());
28416         if(item){
28417             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28418         }
28419     },
28420
28421     onItemClick : function(item, index, e)
28422     {
28423         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28424             return false;
28425         }
28426         if (this.toggleSelect) {
28427             var m = this.isSelected(item) ? 'unselect' : 'select';
28428             //Roo.log(m);
28429             var _t = this;
28430             _t[m](item, true, false);
28431             return true;
28432         }
28433         if(this.multiSelect || this.singleSelect){
28434             if(this.multiSelect && e.shiftKey && this.lastSelection){
28435                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28436             }else{
28437                 this.select(item, this.multiSelect && e.ctrlKey);
28438                 this.lastSelection = item;
28439             }
28440             
28441             if(!this.tickable){
28442                 e.preventDefault();
28443             }
28444             
28445         }
28446         return true;
28447     },
28448
28449     /**
28450      * Get the number of selected nodes.
28451      * @return {Number}
28452      */
28453     getSelectionCount : function(){
28454         return this.selections.length;
28455     },
28456
28457     /**
28458      * Get the currently selected nodes.
28459      * @return {Array} An array of HTMLElements
28460      */
28461     getSelectedNodes : function(){
28462         return this.selections;
28463     },
28464
28465     /**
28466      * Get the indexes of the selected nodes.
28467      * @return {Array}
28468      */
28469     getSelectedIndexes : function(){
28470         var indexes = [], s = this.selections;
28471         for(var i = 0, len = s.length; i < len; i++){
28472             indexes.push(s[i].nodeIndex);
28473         }
28474         return indexes;
28475     },
28476
28477     /**
28478      * Clear all selections
28479      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28480      */
28481     clearSelections : function(suppressEvent){
28482         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28483             this.cmp.elements = this.selections;
28484             this.cmp.removeClass(this.selectedClass);
28485             this.selections = [];
28486             if(!suppressEvent){
28487                 this.fireEvent("selectionchange", this, this.selections);
28488             }
28489         }
28490     },
28491
28492     /**
28493      * Returns true if the passed node is selected
28494      * @param {HTMLElement/Number} node The node or node index
28495      * @return {Boolean}
28496      */
28497     isSelected : function(node){
28498         var s = this.selections;
28499         if(s.length < 1){
28500             return false;
28501         }
28502         node = this.getNode(node);
28503         return s.indexOf(node) !== -1;
28504     },
28505
28506     /**
28507      * Selects nodes.
28508      * @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
28509      * @param {Boolean} keepExisting (optional) true to keep existing selections
28510      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28511      */
28512     select : function(nodeInfo, keepExisting, suppressEvent){
28513         if(nodeInfo instanceof Array){
28514             if(!keepExisting){
28515                 this.clearSelections(true);
28516             }
28517             for(var i = 0, len = nodeInfo.length; i < len; i++){
28518                 this.select(nodeInfo[i], true, true);
28519             }
28520             return;
28521         } 
28522         var node = this.getNode(nodeInfo);
28523         if(!node || this.isSelected(node)){
28524             return; // already selected.
28525         }
28526         if(!keepExisting){
28527             this.clearSelections(true);
28528         }
28529         
28530         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28531             Roo.fly(node).addClass(this.selectedClass);
28532             this.selections.push(node);
28533             if(!suppressEvent){
28534                 this.fireEvent("selectionchange", this, this.selections);
28535             }
28536         }
28537         
28538         
28539     },
28540       /**
28541      * Unselects nodes.
28542      * @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
28543      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28544      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28545      */
28546     unselect : function(nodeInfo, keepExisting, suppressEvent)
28547     {
28548         if(nodeInfo instanceof Array){
28549             Roo.each(this.selections, function(s) {
28550                 this.unselect(s, nodeInfo);
28551             }, this);
28552             return;
28553         }
28554         var node = this.getNode(nodeInfo);
28555         if(!node || !this.isSelected(node)){
28556             //Roo.log("not selected");
28557             return; // not selected.
28558         }
28559         // fireevent???
28560         var ns = [];
28561         Roo.each(this.selections, function(s) {
28562             if (s == node ) {
28563                 Roo.fly(node).removeClass(this.selectedClass);
28564
28565                 return;
28566             }
28567             ns.push(s);
28568         },this);
28569         
28570         this.selections= ns;
28571         this.fireEvent("selectionchange", this, this.selections);
28572     },
28573
28574     /**
28575      * Gets a template node.
28576      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28577      * @return {HTMLElement} The node or null if it wasn't found
28578      */
28579     getNode : function(nodeInfo){
28580         if(typeof nodeInfo == "string"){
28581             return document.getElementById(nodeInfo);
28582         }else if(typeof nodeInfo == "number"){
28583             return this.nodes[nodeInfo];
28584         }
28585         return nodeInfo;
28586     },
28587
28588     /**
28589      * Gets a range template nodes.
28590      * @param {Number} startIndex
28591      * @param {Number} endIndex
28592      * @return {Array} An array of nodes
28593      */
28594     getNodes : function(start, end){
28595         var ns = this.nodes;
28596         start = start || 0;
28597         end = typeof end == "undefined" ? ns.length - 1 : end;
28598         var nodes = [];
28599         if(start <= end){
28600             for(var i = start; i <= end; i++){
28601                 nodes.push(ns[i]);
28602             }
28603         } else{
28604             for(var i = start; i >= end; i--){
28605                 nodes.push(ns[i]);
28606             }
28607         }
28608         return nodes;
28609     },
28610
28611     /**
28612      * Finds the index of the passed node
28613      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28614      * @return {Number} The index of the node or -1
28615      */
28616     indexOf : function(node){
28617         node = this.getNode(node);
28618         if(typeof node.nodeIndex == "number"){
28619             return node.nodeIndex;
28620         }
28621         var ns = this.nodes;
28622         for(var i = 0, len = ns.length; i < len; i++){
28623             if(ns[i] == node){
28624                 return i;
28625             }
28626         }
28627         return -1;
28628     }
28629 });
28630 /*
28631  * Based on:
28632  * Ext JS Library 1.1.1
28633  * Copyright(c) 2006-2007, Ext JS, LLC.
28634  *
28635  * Originally Released Under LGPL - original licence link has changed is not relivant.
28636  *
28637  * Fork - LGPL
28638  * <script type="text/javascript">
28639  */
28640
28641 /**
28642  * @class Roo.JsonView
28643  * @extends Roo.View
28644  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28645 <pre><code>
28646 var view = new Roo.JsonView({
28647     container: "my-element",
28648     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28649     multiSelect: true, 
28650     jsonRoot: "data" 
28651 });
28652
28653 // listen for node click?
28654 view.on("click", function(vw, index, node, e){
28655     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28656 });
28657
28658 // direct load of JSON data
28659 view.load("foobar.php");
28660
28661 // Example from my blog list
28662 var tpl = new Roo.Template(
28663     '&lt;div class="entry"&gt;' +
28664     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28665     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28666     "&lt;/div&gt;&lt;hr /&gt;"
28667 );
28668
28669 var moreView = new Roo.JsonView({
28670     container :  "entry-list", 
28671     template : tpl,
28672     jsonRoot: "posts"
28673 });
28674 moreView.on("beforerender", this.sortEntries, this);
28675 moreView.load({
28676     url: "/blog/get-posts.php",
28677     params: "allposts=true",
28678     text: "Loading Blog Entries..."
28679 });
28680 </code></pre>
28681
28682 * Note: old code is supported with arguments : (container, template, config)
28683
28684
28685  * @constructor
28686  * Create a new JsonView
28687  * 
28688  * @param {Object} config The config object
28689  * 
28690  */
28691 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28692     
28693     
28694     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28695
28696     var um = this.el.getUpdateManager();
28697     um.setRenderer(this);
28698     um.on("update", this.onLoad, this);
28699     um.on("failure", this.onLoadException, this);
28700
28701     /**
28702      * @event beforerender
28703      * Fires before rendering of the downloaded JSON data.
28704      * @param {Roo.JsonView} this
28705      * @param {Object} data The JSON data loaded
28706      */
28707     /**
28708      * @event load
28709      * Fires when data is loaded.
28710      * @param {Roo.JsonView} this
28711      * @param {Object} data The JSON data loaded
28712      * @param {Object} response The raw Connect response object
28713      */
28714     /**
28715      * @event loadexception
28716      * Fires when loading fails.
28717      * @param {Roo.JsonView} this
28718      * @param {Object} response The raw Connect response object
28719      */
28720     this.addEvents({
28721         'beforerender' : true,
28722         'load' : true,
28723         'loadexception' : true
28724     });
28725 };
28726 Roo.extend(Roo.JsonView, Roo.View, {
28727     /**
28728      * @type {String} The root property in the loaded JSON object that contains the data
28729      */
28730     jsonRoot : "",
28731
28732     /**
28733      * Refreshes the view.
28734      */
28735     refresh : function(){
28736         this.clearSelections();
28737         this.el.update("");
28738         var html = [];
28739         var o = this.jsonData;
28740         if(o && o.length > 0){
28741             for(var i = 0, len = o.length; i < len; i++){
28742                 var data = this.prepareData(o[i], i, o);
28743                 html[html.length] = this.tpl.apply(data);
28744             }
28745         }else{
28746             html.push(this.emptyText);
28747         }
28748         this.el.update(html.join(""));
28749         this.nodes = this.el.dom.childNodes;
28750         this.updateIndexes(0);
28751     },
28752
28753     /**
28754      * 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.
28755      * @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:
28756      <pre><code>
28757      view.load({
28758          url: "your-url.php",
28759          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28760          callback: yourFunction,
28761          scope: yourObject, //(optional scope)
28762          discardUrl: false,
28763          nocache: false,
28764          text: "Loading...",
28765          timeout: 30,
28766          scripts: false
28767      });
28768      </code></pre>
28769      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28770      * 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.
28771      * @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}
28772      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28773      * @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.
28774      */
28775     load : function(){
28776         var um = this.el.getUpdateManager();
28777         um.update.apply(um, arguments);
28778     },
28779
28780     // note - render is a standard framework call...
28781     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28782     render : function(el, response){
28783         
28784         this.clearSelections();
28785         this.el.update("");
28786         var o;
28787         try{
28788             if (response != '') {
28789                 o = Roo.util.JSON.decode(response.responseText);
28790                 if(this.jsonRoot){
28791                     
28792                     o = o[this.jsonRoot];
28793                 }
28794             }
28795         } catch(e){
28796         }
28797         /**
28798          * The current JSON data or null
28799          */
28800         this.jsonData = o;
28801         this.beforeRender();
28802         this.refresh();
28803     },
28804
28805 /**
28806  * Get the number of records in the current JSON dataset
28807  * @return {Number}
28808  */
28809     getCount : function(){
28810         return this.jsonData ? this.jsonData.length : 0;
28811     },
28812
28813 /**
28814  * Returns the JSON object for the specified node(s)
28815  * @param {HTMLElement/Array} node The node or an array of nodes
28816  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28817  * you get the JSON object for the node
28818  */
28819     getNodeData : function(node){
28820         if(node instanceof Array){
28821             var data = [];
28822             for(var i = 0, len = node.length; i < len; i++){
28823                 data.push(this.getNodeData(node[i]));
28824             }
28825             return data;
28826         }
28827         return this.jsonData[this.indexOf(node)] || null;
28828     },
28829
28830     beforeRender : function(){
28831         this.snapshot = this.jsonData;
28832         if(this.sortInfo){
28833             this.sort.apply(this, this.sortInfo);
28834         }
28835         this.fireEvent("beforerender", this, this.jsonData);
28836     },
28837
28838     onLoad : function(el, o){
28839         this.fireEvent("load", this, this.jsonData, o);
28840     },
28841
28842     onLoadException : function(el, o){
28843         this.fireEvent("loadexception", this, o);
28844     },
28845
28846 /**
28847  * Filter the data by a specific property.
28848  * @param {String} property A property on your JSON objects
28849  * @param {String/RegExp} value Either string that the property values
28850  * should start with, or a RegExp to test against the property
28851  */
28852     filter : function(property, value){
28853         if(this.jsonData){
28854             var data = [];
28855             var ss = this.snapshot;
28856             if(typeof value == "string"){
28857                 var vlen = value.length;
28858                 if(vlen == 0){
28859                     this.clearFilter();
28860                     return;
28861                 }
28862                 value = value.toLowerCase();
28863                 for(var i = 0, len = ss.length; i < len; i++){
28864                     var o = ss[i];
28865                     if(o[property].substr(0, vlen).toLowerCase() == value){
28866                         data.push(o);
28867                     }
28868                 }
28869             } else if(value.exec){ // regex?
28870                 for(var i = 0, len = ss.length; i < len; i++){
28871                     var o = ss[i];
28872                     if(value.test(o[property])){
28873                         data.push(o);
28874                     }
28875                 }
28876             } else{
28877                 return;
28878             }
28879             this.jsonData = data;
28880             this.refresh();
28881         }
28882     },
28883
28884 /**
28885  * Filter by a function. The passed function will be called with each
28886  * object in the current dataset. If the function returns true the value is kept,
28887  * otherwise it is filtered.
28888  * @param {Function} fn
28889  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28890  */
28891     filterBy : function(fn, scope){
28892         if(this.jsonData){
28893             var data = [];
28894             var ss = this.snapshot;
28895             for(var i = 0, len = ss.length; i < len; i++){
28896                 var o = ss[i];
28897                 if(fn.call(scope || this, o)){
28898                     data.push(o);
28899                 }
28900             }
28901             this.jsonData = data;
28902             this.refresh();
28903         }
28904     },
28905
28906 /**
28907  * Clears the current filter.
28908  */
28909     clearFilter : function(){
28910         if(this.snapshot && this.jsonData != this.snapshot){
28911             this.jsonData = this.snapshot;
28912             this.refresh();
28913         }
28914     },
28915
28916
28917 /**
28918  * Sorts the data for this view and refreshes it.
28919  * @param {String} property A property on your JSON objects to sort on
28920  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28921  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28922  */
28923     sort : function(property, dir, sortType){
28924         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28925         if(this.jsonData){
28926             var p = property;
28927             var dsc = dir && dir.toLowerCase() == "desc";
28928             var f = function(o1, o2){
28929                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28930                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28931                 ;
28932                 if(v1 < v2){
28933                     return dsc ? +1 : -1;
28934                 } else if(v1 > v2){
28935                     return dsc ? -1 : +1;
28936                 } else{
28937                     return 0;
28938                 }
28939             };
28940             this.jsonData.sort(f);
28941             this.refresh();
28942             if(this.jsonData != this.snapshot){
28943                 this.snapshot.sort(f);
28944             }
28945         }
28946     }
28947 });/*
28948  * Based on:
28949  * Ext JS Library 1.1.1
28950  * Copyright(c) 2006-2007, Ext JS, LLC.
28951  *
28952  * Originally Released Under LGPL - original licence link has changed is not relivant.
28953  *
28954  * Fork - LGPL
28955  * <script type="text/javascript">
28956  */
28957  
28958
28959 /**
28960  * @class Roo.ColorPalette
28961  * @extends Roo.Component
28962  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28963  * Here's an example of typical usage:
28964  * <pre><code>
28965 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28966 cp.render('my-div');
28967
28968 cp.on('select', function(palette, selColor){
28969     // do something with selColor
28970 });
28971 </code></pre>
28972  * @constructor
28973  * Create a new ColorPalette
28974  * @param {Object} config The config object
28975  */
28976 Roo.ColorPalette = function(config){
28977     Roo.ColorPalette.superclass.constructor.call(this, config);
28978     this.addEvents({
28979         /**
28980              * @event select
28981              * Fires when a color is selected
28982              * @param {ColorPalette} this
28983              * @param {String} color The 6-digit color hex code (without the # symbol)
28984              */
28985         select: true
28986     });
28987
28988     if(this.handler){
28989         this.on("select", this.handler, this.scope, true);
28990     }
28991 };
28992 Roo.extend(Roo.ColorPalette, Roo.Component, {
28993     /**
28994      * @cfg {String} itemCls
28995      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28996      */
28997     itemCls : "x-color-palette",
28998     /**
28999      * @cfg {String} value
29000      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29001      * the hex codes are case-sensitive.
29002      */
29003     value : null,
29004     clickEvent:'click',
29005     // private
29006     ctype: "Roo.ColorPalette",
29007
29008     /**
29009      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29010      */
29011     allowReselect : false,
29012
29013     /**
29014      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29015      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29016      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29017      * of colors with the width setting until the box is symmetrical.</p>
29018      * <p>You can override individual colors if needed:</p>
29019      * <pre><code>
29020 var cp = new Roo.ColorPalette();
29021 cp.colors[0] = "FF0000";  // change the first box to red
29022 </code></pre>
29023
29024 Or you can provide a custom array of your own for complete control:
29025 <pre><code>
29026 var cp = new Roo.ColorPalette();
29027 cp.colors = ["000000", "993300", "333300"];
29028 </code></pre>
29029      * @type Array
29030      */
29031     colors : [
29032         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29033         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29034         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29035         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29036         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29037     ],
29038
29039     // private
29040     onRender : function(container, position){
29041         var t = new Roo.MasterTemplate(
29042             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29043         );
29044         var c = this.colors;
29045         for(var i = 0, len = c.length; i < len; i++){
29046             t.add([c[i]]);
29047         }
29048         var el = document.createElement("div");
29049         el.className = this.itemCls;
29050         t.overwrite(el);
29051         container.dom.insertBefore(el, position);
29052         this.el = Roo.get(el);
29053         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29054         if(this.clickEvent != 'click'){
29055             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29056         }
29057     },
29058
29059     // private
29060     afterRender : function(){
29061         Roo.ColorPalette.superclass.afterRender.call(this);
29062         if(this.value){
29063             var s = this.value;
29064             this.value = null;
29065             this.select(s);
29066         }
29067     },
29068
29069     // private
29070     handleClick : function(e, t){
29071         e.preventDefault();
29072         if(!this.disabled){
29073             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29074             this.select(c.toUpperCase());
29075         }
29076     },
29077
29078     /**
29079      * Selects the specified color in the palette (fires the select event)
29080      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29081      */
29082     select : function(color){
29083         color = color.replace("#", "");
29084         if(color != this.value || this.allowReselect){
29085             var el = this.el;
29086             if(this.value){
29087                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29088             }
29089             el.child("a.color-"+color).addClass("x-color-palette-sel");
29090             this.value = color;
29091             this.fireEvent("select", this, color);
29092         }
29093     }
29094 });/*
29095  * Based on:
29096  * Ext JS Library 1.1.1
29097  * Copyright(c) 2006-2007, Ext JS, LLC.
29098  *
29099  * Originally Released Under LGPL - original licence link has changed is not relivant.
29100  *
29101  * Fork - LGPL
29102  * <script type="text/javascript">
29103  */
29104  
29105 /**
29106  * @class Roo.DatePicker
29107  * @extends Roo.Component
29108  * Simple date picker class.
29109  * @constructor
29110  * Create a new DatePicker
29111  * @param {Object} config The config object
29112  */
29113 Roo.DatePicker = function(config){
29114     Roo.DatePicker.superclass.constructor.call(this, config);
29115
29116     this.value = config && config.value ?
29117                  config.value.clearTime() : new Date().clearTime();
29118
29119     this.addEvents({
29120         /**
29121              * @event select
29122              * Fires when a date is selected
29123              * @param {DatePicker} this
29124              * @param {Date} date The selected date
29125              */
29126         'select': true,
29127         /**
29128              * @event monthchange
29129              * Fires when the displayed month changes 
29130              * @param {DatePicker} this
29131              * @param {Date} date The selected month
29132              */
29133         'monthchange': true
29134     });
29135
29136     if(this.handler){
29137         this.on("select", this.handler,  this.scope || this);
29138     }
29139     // build the disabledDatesRE
29140     if(!this.disabledDatesRE && this.disabledDates){
29141         var dd = this.disabledDates;
29142         var re = "(?:";
29143         for(var i = 0; i < dd.length; i++){
29144             re += dd[i];
29145             if(i != dd.length-1) {
29146                 re += "|";
29147             }
29148         }
29149         this.disabledDatesRE = new RegExp(re + ")");
29150     }
29151 };
29152
29153 Roo.extend(Roo.DatePicker, Roo.Component, {
29154     /**
29155      * @cfg {String} todayText
29156      * The text to display on the button that selects the current date (defaults to "Today")
29157      */
29158     todayText : "Today",
29159     /**
29160      * @cfg {String} okText
29161      * The text to display on the ok button
29162      */
29163     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29164     /**
29165      * @cfg {String} cancelText
29166      * The text to display on the cancel button
29167      */
29168     cancelText : "Cancel",
29169     /**
29170      * @cfg {String} todayTip
29171      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29172      */
29173     todayTip : "{0} (Spacebar)",
29174     /**
29175      * @cfg {Date} minDate
29176      * Minimum allowable date (JavaScript date object, defaults to null)
29177      */
29178     minDate : null,
29179     /**
29180      * @cfg {Date} maxDate
29181      * Maximum allowable date (JavaScript date object, defaults to null)
29182      */
29183     maxDate : null,
29184     /**
29185      * @cfg {String} minText
29186      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29187      */
29188     minText : "This date is before the minimum date",
29189     /**
29190      * @cfg {String} maxText
29191      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29192      */
29193     maxText : "This date is after the maximum date",
29194     /**
29195      * @cfg {String} format
29196      * The default date format string which can be overriden for localization support.  The format must be
29197      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29198      */
29199     format : "m/d/y",
29200     /**
29201      * @cfg {Array} disabledDays
29202      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29203      */
29204     disabledDays : null,
29205     /**
29206      * @cfg {String} disabledDaysText
29207      * The tooltip to display when the date falls on a disabled day (defaults to "")
29208      */
29209     disabledDaysText : "",
29210     /**
29211      * @cfg {RegExp} disabledDatesRE
29212      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29213      */
29214     disabledDatesRE : null,
29215     /**
29216      * @cfg {String} disabledDatesText
29217      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29218      */
29219     disabledDatesText : "",
29220     /**
29221      * @cfg {Boolean} constrainToViewport
29222      * True to constrain the date picker to the viewport (defaults to true)
29223      */
29224     constrainToViewport : true,
29225     /**
29226      * @cfg {Array} monthNames
29227      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29228      */
29229     monthNames : Date.monthNames,
29230     /**
29231      * @cfg {Array} dayNames
29232      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29233      */
29234     dayNames : Date.dayNames,
29235     /**
29236      * @cfg {String} nextText
29237      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29238      */
29239     nextText: 'Next Month (Control+Right)',
29240     /**
29241      * @cfg {String} prevText
29242      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29243      */
29244     prevText: 'Previous Month (Control+Left)',
29245     /**
29246      * @cfg {String} monthYearText
29247      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29248      */
29249     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29250     /**
29251      * @cfg {Number} startDay
29252      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29253      */
29254     startDay : 0,
29255     /**
29256      * @cfg {Bool} showClear
29257      * Show a clear button (usefull for date form elements that can be blank.)
29258      */
29259     
29260     showClear: false,
29261     
29262     /**
29263      * Sets the value of the date field
29264      * @param {Date} value The date to set
29265      */
29266     setValue : function(value){
29267         var old = this.value;
29268         
29269         if (typeof(value) == 'string') {
29270          
29271             value = Date.parseDate(value, this.format);
29272         }
29273         if (!value) {
29274             value = new Date();
29275         }
29276         
29277         this.value = value.clearTime(true);
29278         if(this.el){
29279             this.update(this.value);
29280         }
29281     },
29282
29283     /**
29284      * Gets the current selected value of the date field
29285      * @return {Date} The selected date
29286      */
29287     getValue : function(){
29288         return this.value;
29289     },
29290
29291     // private
29292     focus : function(){
29293         if(this.el){
29294             this.update(this.activeDate);
29295         }
29296     },
29297
29298     // privateval
29299     onRender : function(container, position){
29300         
29301         var m = [
29302              '<table cellspacing="0">',
29303                 '<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>',
29304                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29305         var dn = this.dayNames;
29306         for(var i = 0; i < 7; i++){
29307             var d = this.startDay+i;
29308             if(d > 6){
29309                 d = d-7;
29310             }
29311             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29312         }
29313         m[m.length] = "</tr></thead><tbody><tr>";
29314         for(var i = 0; i < 42; i++) {
29315             if(i % 7 == 0 && i != 0){
29316                 m[m.length] = "</tr><tr>";
29317             }
29318             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29319         }
29320         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29321             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29322
29323         var el = document.createElement("div");
29324         el.className = "x-date-picker";
29325         el.innerHTML = m.join("");
29326
29327         container.dom.insertBefore(el, position);
29328
29329         this.el = Roo.get(el);
29330         this.eventEl = Roo.get(el.firstChild);
29331
29332         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29333             handler: this.showPrevMonth,
29334             scope: this,
29335             preventDefault:true,
29336             stopDefault:true
29337         });
29338
29339         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29340             handler: this.showNextMonth,
29341             scope: this,
29342             preventDefault:true,
29343             stopDefault:true
29344         });
29345
29346         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29347
29348         this.monthPicker = this.el.down('div.x-date-mp');
29349         this.monthPicker.enableDisplayMode('block');
29350         
29351         var kn = new Roo.KeyNav(this.eventEl, {
29352             "left" : function(e){
29353                 e.ctrlKey ?
29354                     this.showPrevMonth() :
29355                     this.update(this.activeDate.add("d", -1));
29356             },
29357
29358             "right" : function(e){
29359                 e.ctrlKey ?
29360                     this.showNextMonth() :
29361                     this.update(this.activeDate.add("d", 1));
29362             },
29363
29364             "up" : function(e){
29365                 e.ctrlKey ?
29366                     this.showNextYear() :
29367                     this.update(this.activeDate.add("d", -7));
29368             },
29369
29370             "down" : function(e){
29371                 e.ctrlKey ?
29372                     this.showPrevYear() :
29373                     this.update(this.activeDate.add("d", 7));
29374             },
29375
29376             "pageUp" : function(e){
29377                 this.showNextMonth();
29378             },
29379
29380             "pageDown" : function(e){
29381                 this.showPrevMonth();
29382             },
29383
29384             "enter" : function(e){
29385                 e.stopPropagation();
29386                 return true;
29387             },
29388
29389             scope : this
29390         });
29391
29392         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29393
29394         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29395
29396         this.el.unselectable();
29397         
29398         this.cells = this.el.select("table.x-date-inner tbody td");
29399         this.textNodes = this.el.query("table.x-date-inner tbody span");
29400
29401         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29402             text: "&#160;",
29403             tooltip: this.monthYearText
29404         });
29405
29406         this.mbtn.on('click', this.showMonthPicker, this);
29407         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29408
29409
29410         var today = (new Date()).dateFormat(this.format);
29411         
29412         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29413         if (this.showClear) {
29414             baseTb.add( new Roo.Toolbar.Fill());
29415         }
29416         baseTb.add({
29417             text: String.format(this.todayText, today),
29418             tooltip: String.format(this.todayTip, today),
29419             handler: this.selectToday,
29420             scope: this
29421         });
29422         
29423         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29424             
29425         //});
29426         if (this.showClear) {
29427             
29428             baseTb.add( new Roo.Toolbar.Fill());
29429             baseTb.add({
29430                 text: '&#160;',
29431                 cls: 'x-btn-icon x-btn-clear',
29432                 handler: function() {
29433                     //this.value = '';
29434                     this.fireEvent("select", this, '');
29435                 },
29436                 scope: this
29437             });
29438         }
29439         
29440         
29441         if(Roo.isIE){
29442             this.el.repaint();
29443         }
29444         this.update(this.value);
29445     },
29446
29447     createMonthPicker : function(){
29448         if(!this.monthPicker.dom.firstChild){
29449             var buf = ['<table border="0" cellspacing="0">'];
29450             for(var i = 0; i < 6; i++){
29451                 buf.push(
29452                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29453                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29454                     i == 0 ?
29455                     '<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>' :
29456                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29457                 );
29458             }
29459             buf.push(
29460                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29461                     this.okText,
29462                     '</button><button type="button" class="x-date-mp-cancel">',
29463                     this.cancelText,
29464                     '</button></td></tr>',
29465                 '</table>'
29466             );
29467             this.monthPicker.update(buf.join(''));
29468             this.monthPicker.on('click', this.onMonthClick, this);
29469             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29470
29471             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29472             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29473
29474             this.mpMonths.each(function(m, a, i){
29475                 i += 1;
29476                 if((i%2) == 0){
29477                     m.dom.xmonth = 5 + Math.round(i * .5);
29478                 }else{
29479                     m.dom.xmonth = Math.round((i-1) * .5);
29480                 }
29481             });
29482         }
29483     },
29484
29485     showMonthPicker : function(){
29486         this.createMonthPicker();
29487         var size = this.el.getSize();
29488         this.monthPicker.setSize(size);
29489         this.monthPicker.child('table').setSize(size);
29490
29491         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29492         this.updateMPMonth(this.mpSelMonth);
29493         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29494         this.updateMPYear(this.mpSelYear);
29495
29496         this.monthPicker.slideIn('t', {duration:.2});
29497     },
29498
29499     updateMPYear : function(y){
29500         this.mpyear = y;
29501         var ys = this.mpYears.elements;
29502         for(var i = 1; i <= 10; i++){
29503             var td = ys[i-1], y2;
29504             if((i%2) == 0){
29505                 y2 = y + Math.round(i * .5);
29506                 td.firstChild.innerHTML = y2;
29507                 td.xyear = y2;
29508             }else{
29509                 y2 = y - (5-Math.round(i * .5));
29510                 td.firstChild.innerHTML = y2;
29511                 td.xyear = y2;
29512             }
29513             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29514         }
29515     },
29516
29517     updateMPMonth : function(sm){
29518         this.mpMonths.each(function(m, a, i){
29519             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29520         });
29521     },
29522
29523     selectMPMonth: function(m){
29524         
29525     },
29526
29527     onMonthClick : function(e, t){
29528         e.stopEvent();
29529         var el = new Roo.Element(t), pn;
29530         if(el.is('button.x-date-mp-cancel')){
29531             this.hideMonthPicker();
29532         }
29533         else if(el.is('button.x-date-mp-ok')){
29534             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29535             this.hideMonthPicker();
29536         }
29537         else if(pn = el.up('td.x-date-mp-month', 2)){
29538             this.mpMonths.removeClass('x-date-mp-sel');
29539             pn.addClass('x-date-mp-sel');
29540             this.mpSelMonth = pn.dom.xmonth;
29541         }
29542         else if(pn = el.up('td.x-date-mp-year', 2)){
29543             this.mpYears.removeClass('x-date-mp-sel');
29544             pn.addClass('x-date-mp-sel');
29545             this.mpSelYear = pn.dom.xyear;
29546         }
29547         else if(el.is('a.x-date-mp-prev')){
29548             this.updateMPYear(this.mpyear-10);
29549         }
29550         else if(el.is('a.x-date-mp-next')){
29551             this.updateMPYear(this.mpyear+10);
29552         }
29553     },
29554
29555     onMonthDblClick : function(e, t){
29556         e.stopEvent();
29557         var el = new Roo.Element(t), pn;
29558         if(pn = el.up('td.x-date-mp-month', 2)){
29559             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29560             this.hideMonthPicker();
29561         }
29562         else if(pn = el.up('td.x-date-mp-year', 2)){
29563             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29564             this.hideMonthPicker();
29565         }
29566     },
29567
29568     hideMonthPicker : function(disableAnim){
29569         if(this.monthPicker){
29570             if(disableAnim === true){
29571                 this.monthPicker.hide();
29572             }else{
29573                 this.monthPicker.slideOut('t', {duration:.2});
29574             }
29575         }
29576     },
29577
29578     // private
29579     showPrevMonth : function(e){
29580         this.update(this.activeDate.add("mo", -1));
29581     },
29582
29583     // private
29584     showNextMonth : function(e){
29585         this.update(this.activeDate.add("mo", 1));
29586     },
29587
29588     // private
29589     showPrevYear : function(){
29590         this.update(this.activeDate.add("y", -1));
29591     },
29592
29593     // private
29594     showNextYear : function(){
29595         this.update(this.activeDate.add("y", 1));
29596     },
29597
29598     // private
29599     handleMouseWheel : function(e){
29600         var delta = e.getWheelDelta();
29601         if(delta > 0){
29602             this.showPrevMonth();
29603             e.stopEvent();
29604         } else if(delta < 0){
29605             this.showNextMonth();
29606             e.stopEvent();
29607         }
29608     },
29609
29610     // private
29611     handleDateClick : function(e, t){
29612         e.stopEvent();
29613         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29614             this.setValue(new Date(t.dateValue));
29615             this.fireEvent("select", this, this.value);
29616         }
29617     },
29618
29619     // private
29620     selectToday : function(){
29621         this.setValue(new Date().clearTime());
29622         this.fireEvent("select", this, this.value);
29623     },
29624
29625     // private
29626     update : function(date)
29627     {
29628         var vd = this.activeDate;
29629         this.activeDate = date;
29630         if(vd && this.el){
29631             var t = date.getTime();
29632             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29633                 this.cells.removeClass("x-date-selected");
29634                 this.cells.each(function(c){
29635                    if(c.dom.firstChild.dateValue == t){
29636                        c.addClass("x-date-selected");
29637                        setTimeout(function(){
29638                             try{c.dom.firstChild.focus();}catch(e){}
29639                        }, 50);
29640                        return false;
29641                    }
29642                 });
29643                 return;
29644             }
29645         }
29646         
29647         var days = date.getDaysInMonth();
29648         var firstOfMonth = date.getFirstDateOfMonth();
29649         var startingPos = firstOfMonth.getDay()-this.startDay;
29650
29651         if(startingPos <= this.startDay){
29652             startingPos += 7;
29653         }
29654
29655         var pm = date.add("mo", -1);
29656         var prevStart = pm.getDaysInMonth()-startingPos;
29657
29658         var cells = this.cells.elements;
29659         var textEls = this.textNodes;
29660         days += startingPos;
29661
29662         // convert everything to numbers so it's fast
29663         var day = 86400000;
29664         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29665         var today = new Date().clearTime().getTime();
29666         var sel = date.clearTime().getTime();
29667         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29668         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29669         var ddMatch = this.disabledDatesRE;
29670         var ddText = this.disabledDatesText;
29671         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29672         var ddaysText = this.disabledDaysText;
29673         var format = this.format;
29674
29675         var setCellClass = function(cal, cell){
29676             cell.title = "";
29677             var t = d.getTime();
29678             cell.firstChild.dateValue = t;
29679             if(t == today){
29680                 cell.className += " x-date-today";
29681                 cell.title = cal.todayText;
29682             }
29683             if(t == sel){
29684                 cell.className += " x-date-selected";
29685                 setTimeout(function(){
29686                     try{cell.firstChild.focus();}catch(e){}
29687                 }, 50);
29688             }
29689             // disabling
29690             if(t < min) {
29691                 cell.className = " x-date-disabled";
29692                 cell.title = cal.minText;
29693                 return;
29694             }
29695             if(t > max) {
29696                 cell.className = " x-date-disabled";
29697                 cell.title = cal.maxText;
29698                 return;
29699             }
29700             if(ddays){
29701                 if(ddays.indexOf(d.getDay()) != -1){
29702                     cell.title = ddaysText;
29703                     cell.className = " x-date-disabled";
29704                 }
29705             }
29706             if(ddMatch && format){
29707                 var fvalue = d.dateFormat(format);
29708                 if(ddMatch.test(fvalue)){
29709                     cell.title = ddText.replace("%0", fvalue);
29710                     cell.className = " x-date-disabled";
29711                 }
29712             }
29713         };
29714
29715         var i = 0;
29716         for(; i < startingPos; i++) {
29717             textEls[i].innerHTML = (++prevStart);
29718             d.setDate(d.getDate()+1);
29719             cells[i].className = "x-date-prevday";
29720             setCellClass(this, cells[i]);
29721         }
29722         for(; i < days; i++){
29723             intDay = i - startingPos + 1;
29724             textEls[i].innerHTML = (intDay);
29725             d.setDate(d.getDate()+1);
29726             cells[i].className = "x-date-active";
29727             setCellClass(this, cells[i]);
29728         }
29729         var extraDays = 0;
29730         for(; i < 42; i++) {
29731              textEls[i].innerHTML = (++extraDays);
29732              d.setDate(d.getDate()+1);
29733              cells[i].className = "x-date-nextday";
29734              setCellClass(this, cells[i]);
29735         }
29736
29737         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29738         this.fireEvent('monthchange', this, date);
29739         
29740         if(!this.internalRender){
29741             var main = this.el.dom.firstChild;
29742             var w = main.offsetWidth;
29743             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29744             Roo.fly(main).setWidth(w);
29745             this.internalRender = true;
29746             // opera does not respect the auto grow header center column
29747             // then, after it gets a width opera refuses to recalculate
29748             // without a second pass
29749             if(Roo.isOpera && !this.secondPass){
29750                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29751                 this.secondPass = true;
29752                 this.update.defer(10, this, [date]);
29753             }
29754         }
29755         
29756         
29757     }
29758 });        /*
29759  * Based on:
29760  * Ext JS Library 1.1.1
29761  * Copyright(c) 2006-2007, Ext JS, LLC.
29762  *
29763  * Originally Released Under LGPL - original licence link has changed is not relivant.
29764  *
29765  * Fork - LGPL
29766  * <script type="text/javascript">
29767  */
29768 /**
29769  * @class Roo.TabPanel
29770  * @extends Roo.util.Observable
29771  * A lightweight tab container.
29772  * <br><br>
29773  * Usage:
29774  * <pre><code>
29775 // basic tabs 1, built from existing content
29776 var tabs = new Roo.TabPanel("tabs1");
29777 tabs.addTab("script", "View Script");
29778 tabs.addTab("markup", "View Markup");
29779 tabs.activate("script");
29780
29781 // more advanced tabs, built from javascript
29782 var jtabs = new Roo.TabPanel("jtabs");
29783 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29784
29785 // set up the UpdateManager
29786 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29787 var updater = tab2.getUpdateManager();
29788 updater.setDefaultUrl("ajax1.htm");
29789 tab2.on('activate', updater.refresh, updater, true);
29790
29791 // Use setUrl for Ajax loading
29792 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29793 tab3.setUrl("ajax2.htm", null, true);
29794
29795 // Disabled tab
29796 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29797 tab4.disable();
29798
29799 jtabs.activate("jtabs-1");
29800  * </code></pre>
29801  * @constructor
29802  * Create a new TabPanel.
29803  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29804  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29805  */
29806 Roo.TabPanel = function(container, config){
29807     /**
29808     * The container element for this TabPanel.
29809     * @type Roo.Element
29810     */
29811     this.el = Roo.get(container, true);
29812     if(config){
29813         if(typeof config == "boolean"){
29814             this.tabPosition = config ? "bottom" : "top";
29815         }else{
29816             Roo.apply(this, config);
29817         }
29818     }
29819     if(this.tabPosition == "bottom"){
29820         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29821         this.el.addClass("x-tabs-bottom");
29822     }
29823     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29824     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29825     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29826     if(Roo.isIE){
29827         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29828     }
29829     if(this.tabPosition != "bottom"){
29830         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29831          * @type Roo.Element
29832          */
29833         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29834         this.el.addClass("x-tabs-top");
29835     }
29836     this.items = [];
29837
29838     this.bodyEl.setStyle("position", "relative");
29839
29840     this.active = null;
29841     this.activateDelegate = this.activate.createDelegate(this);
29842
29843     this.addEvents({
29844         /**
29845          * @event tabchange
29846          * Fires when the active tab changes
29847          * @param {Roo.TabPanel} this
29848          * @param {Roo.TabPanelItem} activePanel The new active tab
29849          */
29850         "tabchange": true,
29851         /**
29852          * @event beforetabchange
29853          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29854          * @param {Roo.TabPanel} this
29855          * @param {Object} e Set cancel to true on this object to cancel the tab change
29856          * @param {Roo.TabPanelItem} tab The tab being changed to
29857          */
29858         "beforetabchange" : true
29859     });
29860
29861     Roo.EventManager.onWindowResize(this.onResize, this);
29862     this.cpad = this.el.getPadding("lr");
29863     this.hiddenCount = 0;
29864
29865
29866     // toolbar on the tabbar support...
29867     if (this.toolbar) {
29868         var tcfg = this.toolbar;
29869         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29870         this.toolbar = new Roo.Toolbar(tcfg);
29871         if (Roo.isSafari) {
29872             var tbl = tcfg.container.child('table', true);
29873             tbl.setAttribute('width', '100%');
29874         }
29875         
29876     }
29877    
29878
29879
29880     Roo.TabPanel.superclass.constructor.call(this);
29881 };
29882
29883 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29884     /*
29885      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29886      */
29887     tabPosition : "top",
29888     /*
29889      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29890      */
29891     currentTabWidth : 0,
29892     /*
29893      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29894      */
29895     minTabWidth : 40,
29896     /*
29897      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29898      */
29899     maxTabWidth : 250,
29900     /*
29901      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29902      */
29903     preferredTabWidth : 175,
29904     /*
29905      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29906      */
29907     resizeTabs : false,
29908     /*
29909      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29910      */
29911     monitorResize : true,
29912     /*
29913      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29914      */
29915     toolbar : false,
29916
29917     /**
29918      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29919      * @param {String} id The id of the div to use <b>or create</b>
29920      * @param {String} text The text for the tab
29921      * @param {String} content (optional) Content to put in the TabPanelItem body
29922      * @param {Boolean} closable (optional) True to create a close icon on the tab
29923      * @return {Roo.TabPanelItem} The created TabPanelItem
29924      */
29925     addTab : function(id, text, content, closable){
29926         var item = new Roo.TabPanelItem(this, id, text, closable);
29927         this.addTabItem(item);
29928         if(content){
29929             item.setContent(content);
29930         }
29931         return item;
29932     },
29933
29934     /**
29935      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29936      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29937      * @return {Roo.TabPanelItem}
29938      */
29939     getTab : function(id){
29940         return this.items[id];
29941     },
29942
29943     /**
29944      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29945      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29946      */
29947     hideTab : function(id){
29948         var t = this.items[id];
29949         if(!t.isHidden()){
29950            t.setHidden(true);
29951            this.hiddenCount++;
29952            this.autoSizeTabs();
29953         }
29954     },
29955
29956     /**
29957      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29958      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29959      */
29960     unhideTab : function(id){
29961         var t = this.items[id];
29962         if(t.isHidden()){
29963            t.setHidden(false);
29964            this.hiddenCount--;
29965            this.autoSizeTabs();
29966         }
29967     },
29968
29969     /**
29970      * Adds an existing {@link Roo.TabPanelItem}.
29971      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29972      */
29973     addTabItem : function(item){
29974         this.items[item.id] = item;
29975         this.items.push(item);
29976         if(this.resizeTabs){
29977            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29978            this.autoSizeTabs();
29979         }else{
29980             item.autoSize();
29981         }
29982     },
29983
29984     /**
29985      * Removes a {@link Roo.TabPanelItem}.
29986      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29987      */
29988     removeTab : function(id){
29989         var items = this.items;
29990         var tab = items[id];
29991         if(!tab) { return; }
29992         var index = items.indexOf(tab);
29993         if(this.active == tab && items.length > 1){
29994             var newTab = this.getNextAvailable(index);
29995             if(newTab) {
29996                 newTab.activate();
29997             }
29998         }
29999         this.stripEl.dom.removeChild(tab.pnode.dom);
30000         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30001             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30002         }
30003         items.splice(index, 1);
30004         delete this.items[tab.id];
30005         tab.fireEvent("close", tab);
30006         tab.purgeListeners();
30007         this.autoSizeTabs();
30008     },
30009
30010     getNextAvailable : function(start){
30011         var items = this.items;
30012         var index = start;
30013         // look for a next tab that will slide over to
30014         // replace the one being removed
30015         while(index < items.length){
30016             var item = items[++index];
30017             if(item && !item.isHidden()){
30018                 return item;
30019             }
30020         }
30021         // if one isn't found select the previous tab (on the left)
30022         index = start;
30023         while(index >= 0){
30024             var item = items[--index];
30025             if(item && !item.isHidden()){
30026                 return item;
30027             }
30028         }
30029         return null;
30030     },
30031
30032     /**
30033      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30034      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30035      */
30036     disableTab : function(id){
30037         var tab = this.items[id];
30038         if(tab && this.active != tab){
30039             tab.disable();
30040         }
30041     },
30042
30043     /**
30044      * Enables a {@link Roo.TabPanelItem} that is disabled.
30045      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30046      */
30047     enableTab : function(id){
30048         var tab = this.items[id];
30049         tab.enable();
30050     },
30051
30052     /**
30053      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30054      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30055      * @return {Roo.TabPanelItem} The TabPanelItem.
30056      */
30057     activate : function(id){
30058         var tab = this.items[id];
30059         if(!tab){
30060             return null;
30061         }
30062         if(tab == this.active || tab.disabled){
30063             return tab;
30064         }
30065         var e = {};
30066         this.fireEvent("beforetabchange", this, e, tab);
30067         if(e.cancel !== true && !tab.disabled){
30068             if(this.active){
30069                 this.active.hide();
30070             }
30071             this.active = this.items[id];
30072             this.active.show();
30073             this.fireEvent("tabchange", this, this.active);
30074         }
30075         return tab;
30076     },
30077
30078     /**
30079      * Gets the active {@link Roo.TabPanelItem}.
30080      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30081      */
30082     getActiveTab : function(){
30083         return this.active;
30084     },
30085
30086     /**
30087      * Updates the tab body element to fit the height of the container element
30088      * for overflow scrolling
30089      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30090      */
30091     syncHeight : function(targetHeight){
30092         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30093         var bm = this.bodyEl.getMargins();
30094         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30095         this.bodyEl.setHeight(newHeight);
30096         return newHeight;
30097     },
30098
30099     onResize : function(){
30100         if(this.monitorResize){
30101             this.autoSizeTabs();
30102         }
30103     },
30104
30105     /**
30106      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30107      */
30108     beginUpdate : function(){
30109         this.updating = true;
30110     },
30111
30112     /**
30113      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30114      */
30115     endUpdate : function(){
30116         this.updating = false;
30117         this.autoSizeTabs();
30118     },
30119
30120     /**
30121      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30122      */
30123     autoSizeTabs : function(){
30124         var count = this.items.length;
30125         var vcount = count - this.hiddenCount;
30126         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30127             return;
30128         }
30129         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30130         var availWidth = Math.floor(w / vcount);
30131         var b = this.stripBody;
30132         if(b.getWidth() > w){
30133             var tabs = this.items;
30134             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30135             if(availWidth < this.minTabWidth){
30136                 /*if(!this.sleft){    // incomplete scrolling code
30137                     this.createScrollButtons();
30138                 }
30139                 this.showScroll();
30140                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30141             }
30142         }else{
30143             if(this.currentTabWidth < this.preferredTabWidth){
30144                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30145             }
30146         }
30147     },
30148
30149     /**
30150      * Returns the number of tabs in this TabPanel.
30151      * @return {Number}
30152      */
30153      getCount : function(){
30154          return this.items.length;
30155      },
30156
30157     /**
30158      * Resizes all the tabs to the passed width
30159      * @param {Number} The new width
30160      */
30161     setTabWidth : function(width){
30162         this.currentTabWidth = width;
30163         for(var i = 0, len = this.items.length; i < len; i++) {
30164                 if(!this.items[i].isHidden()) {
30165                 this.items[i].setWidth(width);
30166             }
30167         }
30168     },
30169
30170     /**
30171      * Destroys this TabPanel
30172      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30173      */
30174     destroy : function(removeEl){
30175         Roo.EventManager.removeResizeListener(this.onResize, this);
30176         for(var i = 0, len = this.items.length; i < len; i++){
30177             this.items[i].purgeListeners();
30178         }
30179         if(removeEl === true){
30180             this.el.update("");
30181             this.el.remove();
30182         }
30183     }
30184 });
30185
30186 /**
30187  * @class Roo.TabPanelItem
30188  * @extends Roo.util.Observable
30189  * Represents an individual item (tab plus body) in a TabPanel.
30190  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30191  * @param {String} id The id of this TabPanelItem
30192  * @param {String} text The text for the tab of this TabPanelItem
30193  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30194  */
30195 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30196     /**
30197      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30198      * @type Roo.TabPanel
30199      */
30200     this.tabPanel = tabPanel;
30201     /**
30202      * The id for this TabPanelItem
30203      * @type String
30204      */
30205     this.id = id;
30206     /** @private */
30207     this.disabled = false;
30208     /** @private */
30209     this.text = text;
30210     /** @private */
30211     this.loaded = false;
30212     this.closable = closable;
30213
30214     /**
30215      * The body element for this TabPanelItem.
30216      * @type Roo.Element
30217      */
30218     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30219     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30220     this.bodyEl.setStyle("display", "block");
30221     this.bodyEl.setStyle("zoom", "1");
30222     this.hideAction();
30223
30224     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30225     /** @private */
30226     this.el = Roo.get(els.el, true);
30227     this.inner = Roo.get(els.inner, true);
30228     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30229     this.pnode = Roo.get(els.el.parentNode, true);
30230     this.el.on("mousedown", this.onTabMouseDown, this);
30231     this.el.on("click", this.onTabClick, this);
30232     /** @private */
30233     if(closable){
30234         var c = Roo.get(els.close, true);
30235         c.dom.title = this.closeText;
30236         c.addClassOnOver("close-over");
30237         c.on("click", this.closeClick, this);
30238      }
30239
30240     this.addEvents({
30241          /**
30242          * @event activate
30243          * Fires when this tab becomes the active tab.
30244          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30245          * @param {Roo.TabPanelItem} this
30246          */
30247         "activate": true,
30248         /**
30249          * @event beforeclose
30250          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30251          * @param {Roo.TabPanelItem} this
30252          * @param {Object} e Set cancel to true on this object to cancel the close.
30253          */
30254         "beforeclose": true,
30255         /**
30256          * @event close
30257          * Fires when this tab is closed.
30258          * @param {Roo.TabPanelItem} this
30259          */
30260          "close": true,
30261         /**
30262          * @event deactivate
30263          * Fires when this tab is no longer the active tab.
30264          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30265          * @param {Roo.TabPanelItem} this
30266          */
30267          "deactivate" : true
30268     });
30269     this.hidden = false;
30270
30271     Roo.TabPanelItem.superclass.constructor.call(this);
30272 };
30273
30274 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30275     purgeListeners : function(){
30276        Roo.util.Observable.prototype.purgeListeners.call(this);
30277        this.el.removeAllListeners();
30278     },
30279     /**
30280      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30281      */
30282     show : function(){
30283         this.pnode.addClass("on");
30284         this.showAction();
30285         if(Roo.isOpera){
30286             this.tabPanel.stripWrap.repaint();
30287         }
30288         this.fireEvent("activate", this.tabPanel, this);
30289     },
30290
30291     /**
30292      * Returns true if this tab is the active tab.
30293      * @return {Boolean}
30294      */
30295     isActive : function(){
30296         return this.tabPanel.getActiveTab() == this;
30297     },
30298
30299     /**
30300      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30301      */
30302     hide : function(){
30303         this.pnode.removeClass("on");
30304         this.hideAction();
30305         this.fireEvent("deactivate", this.tabPanel, this);
30306     },
30307
30308     hideAction : function(){
30309         this.bodyEl.hide();
30310         this.bodyEl.setStyle("position", "absolute");
30311         this.bodyEl.setLeft("-20000px");
30312         this.bodyEl.setTop("-20000px");
30313     },
30314
30315     showAction : function(){
30316         this.bodyEl.setStyle("position", "relative");
30317         this.bodyEl.setTop("");
30318         this.bodyEl.setLeft("");
30319         this.bodyEl.show();
30320     },
30321
30322     /**
30323      * Set the tooltip for the tab.
30324      * @param {String} tooltip The tab's tooltip
30325      */
30326     setTooltip : function(text){
30327         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30328             this.textEl.dom.qtip = text;
30329             this.textEl.dom.removeAttribute('title');
30330         }else{
30331             this.textEl.dom.title = text;
30332         }
30333     },
30334
30335     onTabClick : function(e){
30336         e.preventDefault();
30337         this.tabPanel.activate(this.id);
30338     },
30339
30340     onTabMouseDown : function(e){
30341         e.preventDefault();
30342         this.tabPanel.activate(this.id);
30343     },
30344
30345     getWidth : function(){
30346         return this.inner.getWidth();
30347     },
30348
30349     setWidth : function(width){
30350         var iwidth = width - this.pnode.getPadding("lr");
30351         this.inner.setWidth(iwidth);
30352         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30353         this.pnode.setWidth(width);
30354     },
30355
30356     /**
30357      * Show or hide the tab
30358      * @param {Boolean} hidden True to hide or false to show.
30359      */
30360     setHidden : function(hidden){
30361         this.hidden = hidden;
30362         this.pnode.setStyle("display", hidden ? "none" : "");
30363     },
30364
30365     /**
30366      * Returns true if this tab is "hidden"
30367      * @return {Boolean}
30368      */
30369     isHidden : function(){
30370         return this.hidden;
30371     },
30372
30373     /**
30374      * Returns the text for this tab
30375      * @return {String}
30376      */
30377     getText : function(){
30378         return this.text;
30379     },
30380
30381     autoSize : function(){
30382         //this.el.beginMeasure();
30383         this.textEl.setWidth(1);
30384         /*
30385          *  #2804 [new] Tabs in Roojs
30386          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30387          */
30388         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30389         //this.el.endMeasure();
30390     },
30391
30392     /**
30393      * Sets the text for the tab (Note: this also sets the tooltip text)
30394      * @param {String} text The tab's text and tooltip
30395      */
30396     setText : function(text){
30397         this.text = text;
30398         this.textEl.update(text);
30399         this.setTooltip(text);
30400         if(!this.tabPanel.resizeTabs){
30401             this.autoSize();
30402         }
30403     },
30404     /**
30405      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30406      */
30407     activate : function(){
30408         this.tabPanel.activate(this.id);
30409     },
30410
30411     /**
30412      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30413      */
30414     disable : function(){
30415         if(this.tabPanel.active != this){
30416             this.disabled = true;
30417             this.pnode.addClass("disabled");
30418         }
30419     },
30420
30421     /**
30422      * Enables this TabPanelItem if it was previously disabled.
30423      */
30424     enable : function(){
30425         this.disabled = false;
30426         this.pnode.removeClass("disabled");
30427     },
30428
30429     /**
30430      * Sets the content for this TabPanelItem.
30431      * @param {String} content The content
30432      * @param {Boolean} loadScripts true to look for and load scripts
30433      */
30434     setContent : function(content, loadScripts){
30435         this.bodyEl.update(content, loadScripts);
30436     },
30437
30438     /**
30439      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30440      * @return {Roo.UpdateManager} The UpdateManager
30441      */
30442     getUpdateManager : function(){
30443         return this.bodyEl.getUpdateManager();
30444     },
30445
30446     /**
30447      * Set a URL to be used to load the content for this TabPanelItem.
30448      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30449      * @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)
30450      * @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)
30451      * @return {Roo.UpdateManager} The UpdateManager
30452      */
30453     setUrl : function(url, params, loadOnce){
30454         if(this.refreshDelegate){
30455             this.un('activate', this.refreshDelegate);
30456         }
30457         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30458         this.on("activate", this.refreshDelegate);
30459         return this.bodyEl.getUpdateManager();
30460     },
30461
30462     /** @private */
30463     _handleRefresh : function(url, params, loadOnce){
30464         if(!loadOnce || !this.loaded){
30465             var updater = this.bodyEl.getUpdateManager();
30466             updater.update(url, params, this._setLoaded.createDelegate(this));
30467         }
30468     },
30469
30470     /**
30471      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30472      *   Will fail silently if the setUrl method has not been called.
30473      *   This does not activate the panel, just updates its content.
30474      */
30475     refresh : function(){
30476         if(this.refreshDelegate){
30477            this.loaded = false;
30478            this.refreshDelegate();
30479         }
30480     },
30481
30482     /** @private */
30483     _setLoaded : function(){
30484         this.loaded = true;
30485     },
30486
30487     /** @private */
30488     closeClick : function(e){
30489         var o = {};
30490         e.stopEvent();
30491         this.fireEvent("beforeclose", this, o);
30492         if(o.cancel !== true){
30493             this.tabPanel.removeTab(this.id);
30494         }
30495     },
30496     /**
30497      * The text displayed in the tooltip for the close icon.
30498      * @type String
30499      */
30500     closeText : "Close this tab"
30501 });
30502
30503 /** @private */
30504 Roo.TabPanel.prototype.createStrip = function(container){
30505     var strip = document.createElement("div");
30506     strip.className = "x-tabs-wrap";
30507     container.appendChild(strip);
30508     return strip;
30509 };
30510 /** @private */
30511 Roo.TabPanel.prototype.createStripList = function(strip){
30512     // div wrapper for retard IE
30513     // returns the "tr" element.
30514     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30515         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30516         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30517     return strip.firstChild.firstChild.firstChild.firstChild;
30518 };
30519 /** @private */
30520 Roo.TabPanel.prototype.createBody = function(container){
30521     var body = document.createElement("div");
30522     Roo.id(body, "tab-body");
30523     Roo.fly(body).addClass("x-tabs-body");
30524     container.appendChild(body);
30525     return body;
30526 };
30527 /** @private */
30528 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30529     var body = Roo.getDom(id);
30530     if(!body){
30531         body = document.createElement("div");
30532         body.id = id;
30533     }
30534     Roo.fly(body).addClass("x-tabs-item-body");
30535     bodyEl.insertBefore(body, bodyEl.firstChild);
30536     return body;
30537 };
30538 /** @private */
30539 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30540     var td = document.createElement("td");
30541     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30542     //stripEl.appendChild(td);
30543     if(closable){
30544         td.className = "x-tabs-closable";
30545         if(!this.closeTpl){
30546             this.closeTpl = new Roo.Template(
30547                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30548                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30549                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30550             );
30551         }
30552         var el = this.closeTpl.overwrite(td, {"text": text});
30553         var close = el.getElementsByTagName("div")[0];
30554         var inner = el.getElementsByTagName("em")[0];
30555         return {"el": el, "close": close, "inner": inner};
30556     } else {
30557         if(!this.tabTpl){
30558             this.tabTpl = new Roo.Template(
30559                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30560                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30561             );
30562         }
30563         var el = this.tabTpl.overwrite(td, {"text": text});
30564         var inner = el.getElementsByTagName("em")[0];
30565         return {"el": el, "inner": inner};
30566     }
30567 };/*
30568  * Based on:
30569  * Ext JS Library 1.1.1
30570  * Copyright(c) 2006-2007, Ext JS, LLC.
30571  *
30572  * Originally Released Under LGPL - original licence link has changed is not relivant.
30573  *
30574  * Fork - LGPL
30575  * <script type="text/javascript">
30576  */
30577
30578 /**
30579  * @class Roo.Button
30580  * @extends Roo.util.Observable
30581  * Simple Button class
30582  * @cfg {String} text The button text
30583  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30584  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30585  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30586  * @cfg {Object} scope The scope of the handler
30587  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30588  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30589  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30590  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30591  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30592  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30593    applies if enableToggle = true)
30594  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30595  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30596   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30597  * @constructor
30598  * Create a new button
30599  * @param {Object} config The config object
30600  */
30601 Roo.Button = function(renderTo, config)
30602 {
30603     if (!config) {
30604         config = renderTo;
30605         renderTo = config.renderTo || false;
30606     }
30607     
30608     Roo.apply(this, config);
30609     this.addEvents({
30610         /**
30611              * @event click
30612              * Fires when this button is clicked
30613              * @param {Button} this
30614              * @param {EventObject} e The click event
30615              */
30616             "click" : true,
30617         /**
30618              * @event toggle
30619              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30620              * @param {Button} this
30621              * @param {Boolean} pressed
30622              */
30623             "toggle" : true,
30624         /**
30625              * @event mouseover
30626              * Fires when the mouse hovers over the button
30627              * @param {Button} this
30628              * @param {Event} e The event object
30629              */
30630         'mouseover' : true,
30631         /**
30632              * @event mouseout
30633              * Fires when the mouse exits the button
30634              * @param {Button} this
30635              * @param {Event} e The event object
30636              */
30637         'mouseout': true,
30638          /**
30639              * @event render
30640              * Fires when the button is rendered
30641              * @param {Button} this
30642              */
30643         'render': true
30644     });
30645     if(this.menu){
30646         this.menu = Roo.menu.MenuMgr.get(this.menu);
30647     }
30648     // register listeners first!!  - so render can be captured..
30649     Roo.util.Observable.call(this);
30650     if(renderTo){
30651         this.render(renderTo);
30652     }
30653     
30654   
30655 };
30656
30657 Roo.extend(Roo.Button, Roo.util.Observable, {
30658     /**
30659      * 
30660      */
30661     
30662     /**
30663      * Read-only. True if this button is hidden
30664      * @type Boolean
30665      */
30666     hidden : false,
30667     /**
30668      * Read-only. True if this button is disabled
30669      * @type Boolean
30670      */
30671     disabled : false,
30672     /**
30673      * Read-only. True if this button is pressed (only if enableToggle = true)
30674      * @type Boolean
30675      */
30676     pressed : false,
30677
30678     /**
30679      * @cfg {Number} tabIndex 
30680      * The DOM tabIndex for this button (defaults to undefined)
30681      */
30682     tabIndex : undefined,
30683
30684     /**
30685      * @cfg {Boolean} enableToggle
30686      * True to enable pressed/not pressed toggling (defaults to false)
30687      */
30688     enableToggle: false,
30689     /**
30690      * @cfg {Roo.menu.Menu} menu
30691      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30692      */
30693     menu : undefined,
30694     /**
30695      * @cfg {String} menuAlign
30696      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30697      */
30698     menuAlign : "tl-bl?",
30699
30700     /**
30701      * @cfg {String} iconCls
30702      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30703      */
30704     iconCls : undefined,
30705     /**
30706      * @cfg {String} type
30707      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30708      */
30709     type : 'button',
30710
30711     // private
30712     menuClassTarget: 'tr',
30713
30714     /**
30715      * @cfg {String} clickEvent
30716      * The type of event to map to the button's event handler (defaults to 'click')
30717      */
30718     clickEvent : 'click',
30719
30720     /**
30721      * @cfg {Boolean} handleMouseEvents
30722      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30723      */
30724     handleMouseEvents : true,
30725
30726     /**
30727      * @cfg {String} tooltipType
30728      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30729      */
30730     tooltipType : 'qtip',
30731
30732     /**
30733      * @cfg {String} cls
30734      * A CSS class to apply to the button's main element.
30735      */
30736     
30737     /**
30738      * @cfg {Roo.Template} template (Optional)
30739      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30740      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30741      * require code modifications if required elements (e.g. a button) aren't present.
30742      */
30743
30744     // private
30745     render : function(renderTo){
30746         var btn;
30747         if(this.hideParent){
30748             this.parentEl = Roo.get(renderTo);
30749         }
30750         if(!this.dhconfig){
30751             if(!this.template){
30752                 if(!Roo.Button.buttonTemplate){
30753                     // hideous table template
30754                     Roo.Button.buttonTemplate = new Roo.Template(
30755                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30756                         '<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>',
30757                         "</tr></tbody></table>");
30758                 }
30759                 this.template = Roo.Button.buttonTemplate;
30760             }
30761             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30762             var btnEl = btn.child("button:first");
30763             btnEl.on('focus', this.onFocus, this);
30764             btnEl.on('blur', this.onBlur, this);
30765             if(this.cls){
30766                 btn.addClass(this.cls);
30767             }
30768             if(this.icon){
30769                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30770             }
30771             if(this.iconCls){
30772                 btnEl.addClass(this.iconCls);
30773                 if(!this.cls){
30774                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30775                 }
30776             }
30777             if(this.tabIndex !== undefined){
30778                 btnEl.dom.tabIndex = this.tabIndex;
30779             }
30780             if(this.tooltip){
30781                 if(typeof this.tooltip == 'object'){
30782                     Roo.QuickTips.tips(Roo.apply({
30783                           target: btnEl.id
30784                     }, this.tooltip));
30785                 } else {
30786                     btnEl.dom[this.tooltipType] = this.tooltip;
30787                 }
30788             }
30789         }else{
30790             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30791         }
30792         this.el = btn;
30793         if(this.id){
30794             this.el.dom.id = this.el.id = this.id;
30795         }
30796         if(this.menu){
30797             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30798             this.menu.on("show", this.onMenuShow, this);
30799             this.menu.on("hide", this.onMenuHide, this);
30800         }
30801         btn.addClass("x-btn");
30802         if(Roo.isIE && !Roo.isIE7){
30803             this.autoWidth.defer(1, this);
30804         }else{
30805             this.autoWidth();
30806         }
30807         if(this.handleMouseEvents){
30808             btn.on("mouseover", this.onMouseOver, this);
30809             btn.on("mouseout", this.onMouseOut, this);
30810             btn.on("mousedown", this.onMouseDown, this);
30811         }
30812         btn.on(this.clickEvent, this.onClick, this);
30813         //btn.on("mouseup", this.onMouseUp, this);
30814         if(this.hidden){
30815             this.hide();
30816         }
30817         if(this.disabled){
30818             this.disable();
30819         }
30820         Roo.ButtonToggleMgr.register(this);
30821         if(this.pressed){
30822             this.el.addClass("x-btn-pressed");
30823         }
30824         if(this.repeat){
30825             var repeater = new Roo.util.ClickRepeater(btn,
30826                 typeof this.repeat == "object" ? this.repeat : {}
30827             );
30828             repeater.on("click", this.onClick,  this);
30829         }
30830         
30831         this.fireEvent('render', this);
30832         
30833     },
30834     /**
30835      * Returns the button's underlying element
30836      * @return {Roo.Element} The element
30837      */
30838     getEl : function(){
30839         return this.el;  
30840     },
30841     
30842     /**
30843      * Destroys this Button and removes any listeners.
30844      */
30845     destroy : function(){
30846         Roo.ButtonToggleMgr.unregister(this);
30847         this.el.removeAllListeners();
30848         this.purgeListeners();
30849         this.el.remove();
30850     },
30851
30852     // private
30853     autoWidth : function(){
30854         if(this.el){
30855             this.el.setWidth("auto");
30856             if(Roo.isIE7 && Roo.isStrict){
30857                 var ib = this.el.child('button');
30858                 if(ib && ib.getWidth() > 20){
30859                     ib.clip();
30860                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30861                 }
30862             }
30863             if(this.minWidth){
30864                 if(this.hidden){
30865                     this.el.beginMeasure();
30866                 }
30867                 if(this.el.getWidth() < this.minWidth){
30868                     this.el.setWidth(this.minWidth);
30869                 }
30870                 if(this.hidden){
30871                     this.el.endMeasure();
30872                 }
30873             }
30874         }
30875     },
30876
30877     /**
30878      * Assigns this button's click handler
30879      * @param {Function} handler The function to call when the button is clicked
30880      * @param {Object} scope (optional) Scope for the function passed in
30881      */
30882     setHandler : function(handler, scope){
30883         this.handler = handler;
30884         this.scope = scope;  
30885     },
30886     
30887     /**
30888      * Sets this button's text
30889      * @param {String} text The button text
30890      */
30891     setText : function(text){
30892         this.text = text;
30893         if(this.el){
30894             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30895         }
30896         this.autoWidth();
30897     },
30898     
30899     /**
30900      * Gets the text for this button
30901      * @return {String} The button text
30902      */
30903     getText : function(){
30904         return this.text;  
30905     },
30906     
30907     /**
30908      * Show this button
30909      */
30910     show: function(){
30911         this.hidden = false;
30912         if(this.el){
30913             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30914         }
30915     },
30916     
30917     /**
30918      * Hide this button
30919      */
30920     hide: function(){
30921         this.hidden = true;
30922         if(this.el){
30923             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30924         }
30925     },
30926     
30927     /**
30928      * Convenience function for boolean show/hide
30929      * @param {Boolean} visible True to show, false to hide
30930      */
30931     setVisible: function(visible){
30932         if(visible) {
30933             this.show();
30934         }else{
30935             this.hide();
30936         }
30937     },
30938     
30939     /**
30940      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30941      * @param {Boolean} state (optional) Force a particular state
30942      */
30943     toggle : function(state){
30944         state = state === undefined ? !this.pressed : state;
30945         if(state != this.pressed){
30946             if(state){
30947                 this.el.addClass("x-btn-pressed");
30948                 this.pressed = true;
30949                 this.fireEvent("toggle", this, true);
30950             }else{
30951                 this.el.removeClass("x-btn-pressed");
30952                 this.pressed = false;
30953                 this.fireEvent("toggle", this, false);
30954             }
30955             if(this.toggleHandler){
30956                 this.toggleHandler.call(this.scope || this, this, state);
30957             }
30958         }
30959     },
30960     
30961     /**
30962      * Focus the button
30963      */
30964     focus : function(){
30965         this.el.child('button:first').focus();
30966     },
30967     
30968     /**
30969      * Disable this button
30970      */
30971     disable : function(){
30972         if(this.el){
30973             this.el.addClass("x-btn-disabled");
30974         }
30975         this.disabled = true;
30976     },
30977     
30978     /**
30979      * Enable this button
30980      */
30981     enable : function(){
30982         if(this.el){
30983             this.el.removeClass("x-btn-disabled");
30984         }
30985         this.disabled = false;
30986     },
30987
30988     /**
30989      * Convenience function for boolean enable/disable
30990      * @param {Boolean} enabled True to enable, false to disable
30991      */
30992     setDisabled : function(v){
30993         this[v !== true ? "enable" : "disable"]();
30994     },
30995
30996     // private
30997     onClick : function(e)
30998     {
30999         if(e){
31000             e.preventDefault();
31001         }
31002         if(e.button != 0){
31003             return;
31004         }
31005         if(!this.disabled){
31006             if(this.enableToggle){
31007                 this.toggle();
31008             }
31009             if(this.menu && !this.menu.isVisible()){
31010                 this.menu.show(this.el, this.menuAlign);
31011             }
31012             this.fireEvent("click", this, e);
31013             if(this.handler){
31014                 this.el.removeClass("x-btn-over");
31015                 this.handler.call(this.scope || this, this, e);
31016             }
31017         }
31018     },
31019     // private
31020     onMouseOver : function(e){
31021         if(!this.disabled){
31022             this.el.addClass("x-btn-over");
31023             this.fireEvent('mouseover', this, e);
31024         }
31025     },
31026     // private
31027     onMouseOut : function(e){
31028         if(!e.within(this.el,  true)){
31029             this.el.removeClass("x-btn-over");
31030             this.fireEvent('mouseout', this, e);
31031         }
31032     },
31033     // private
31034     onFocus : function(e){
31035         if(!this.disabled){
31036             this.el.addClass("x-btn-focus");
31037         }
31038     },
31039     // private
31040     onBlur : function(e){
31041         this.el.removeClass("x-btn-focus");
31042     },
31043     // private
31044     onMouseDown : function(e){
31045         if(!this.disabled && e.button == 0){
31046             this.el.addClass("x-btn-click");
31047             Roo.get(document).on('mouseup', this.onMouseUp, this);
31048         }
31049     },
31050     // private
31051     onMouseUp : function(e){
31052         if(e.button == 0){
31053             this.el.removeClass("x-btn-click");
31054             Roo.get(document).un('mouseup', this.onMouseUp, this);
31055         }
31056     },
31057     // private
31058     onMenuShow : function(e){
31059         this.el.addClass("x-btn-menu-active");
31060     },
31061     // private
31062     onMenuHide : function(e){
31063         this.el.removeClass("x-btn-menu-active");
31064     }   
31065 });
31066
31067 // Private utility class used by Button
31068 Roo.ButtonToggleMgr = function(){
31069    var groups = {};
31070    
31071    function toggleGroup(btn, state){
31072        if(state){
31073            var g = groups[btn.toggleGroup];
31074            for(var i = 0, l = g.length; i < l; i++){
31075                if(g[i] != btn){
31076                    g[i].toggle(false);
31077                }
31078            }
31079        }
31080    }
31081    
31082    return {
31083        register : function(btn){
31084            if(!btn.toggleGroup){
31085                return;
31086            }
31087            var g = groups[btn.toggleGroup];
31088            if(!g){
31089                g = groups[btn.toggleGroup] = [];
31090            }
31091            g.push(btn);
31092            btn.on("toggle", toggleGroup);
31093        },
31094        
31095        unregister : function(btn){
31096            if(!btn.toggleGroup){
31097                return;
31098            }
31099            var g = groups[btn.toggleGroup];
31100            if(g){
31101                g.remove(btn);
31102                btn.un("toggle", toggleGroup);
31103            }
31104        }
31105    };
31106 }();/*
31107  * Based on:
31108  * Ext JS Library 1.1.1
31109  * Copyright(c) 2006-2007, Ext JS, LLC.
31110  *
31111  * Originally Released Under LGPL - original licence link has changed is not relivant.
31112  *
31113  * Fork - LGPL
31114  * <script type="text/javascript">
31115  */
31116  
31117 /**
31118  * @class Roo.SplitButton
31119  * @extends Roo.Button
31120  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31121  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31122  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31123  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31124  * @cfg {String} arrowTooltip The title attribute of the arrow
31125  * @constructor
31126  * Create a new menu button
31127  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31128  * @param {Object} config The config object
31129  */
31130 Roo.SplitButton = function(renderTo, config){
31131     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31132     /**
31133      * @event arrowclick
31134      * Fires when this button's arrow is clicked
31135      * @param {SplitButton} this
31136      * @param {EventObject} e The click event
31137      */
31138     this.addEvents({"arrowclick":true});
31139 };
31140
31141 Roo.extend(Roo.SplitButton, Roo.Button, {
31142     render : function(renderTo){
31143         // this is one sweet looking template!
31144         var tpl = new Roo.Template(
31145             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31146             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31147             '<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>',
31148             "</tbody></table></td><td>",
31149             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31150             '<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>',
31151             "</tbody></table></td></tr></table>"
31152         );
31153         var btn = tpl.append(renderTo, [this.text, this.type], true);
31154         var btnEl = btn.child("button");
31155         if(this.cls){
31156             btn.addClass(this.cls);
31157         }
31158         if(this.icon){
31159             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31160         }
31161         if(this.iconCls){
31162             btnEl.addClass(this.iconCls);
31163             if(!this.cls){
31164                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31165             }
31166         }
31167         this.el = btn;
31168         if(this.handleMouseEvents){
31169             btn.on("mouseover", this.onMouseOver, this);
31170             btn.on("mouseout", this.onMouseOut, this);
31171             btn.on("mousedown", this.onMouseDown, this);
31172             btn.on("mouseup", this.onMouseUp, this);
31173         }
31174         btn.on(this.clickEvent, this.onClick, this);
31175         if(this.tooltip){
31176             if(typeof this.tooltip == 'object'){
31177                 Roo.QuickTips.tips(Roo.apply({
31178                       target: btnEl.id
31179                 }, this.tooltip));
31180             } else {
31181                 btnEl.dom[this.tooltipType] = this.tooltip;
31182             }
31183         }
31184         if(this.arrowTooltip){
31185             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31186         }
31187         if(this.hidden){
31188             this.hide();
31189         }
31190         if(this.disabled){
31191             this.disable();
31192         }
31193         if(this.pressed){
31194             this.el.addClass("x-btn-pressed");
31195         }
31196         if(Roo.isIE && !Roo.isIE7){
31197             this.autoWidth.defer(1, this);
31198         }else{
31199             this.autoWidth();
31200         }
31201         if(this.menu){
31202             this.menu.on("show", this.onMenuShow, this);
31203             this.menu.on("hide", this.onMenuHide, this);
31204         }
31205         this.fireEvent('render', this);
31206     },
31207
31208     // private
31209     autoWidth : function(){
31210         if(this.el){
31211             var tbl = this.el.child("table:first");
31212             var tbl2 = this.el.child("table:last");
31213             this.el.setWidth("auto");
31214             tbl.setWidth("auto");
31215             if(Roo.isIE7 && Roo.isStrict){
31216                 var ib = this.el.child('button:first');
31217                 if(ib && ib.getWidth() > 20){
31218                     ib.clip();
31219                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31220                 }
31221             }
31222             if(this.minWidth){
31223                 if(this.hidden){
31224                     this.el.beginMeasure();
31225                 }
31226                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31227                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31228                 }
31229                 if(this.hidden){
31230                     this.el.endMeasure();
31231                 }
31232             }
31233             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31234         } 
31235     },
31236     /**
31237      * Sets this button's click handler
31238      * @param {Function} handler The function to call when the button is clicked
31239      * @param {Object} scope (optional) Scope for the function passed above
31240      */
31241     setHandler : function(handler, scope){
31242         this.handler = handler;
31243         this.scope = scope;  
31244     },
31245     
31246     /**
31247      * Sets this button's arrow click handler
31248      * @param {Function} handler The function to call when the arrow is clicked
31249      * @param {Object} scope (optional) Scope for the function passed above
31250      */
31251     setArrowHandler : function(handler, scope){
31252         this.arrowHandler = handler;
31253         this.scope = scope;  
31254     },
31255     
31256     /**
31257      * Focus the button
31258      */
31259     focus : function(){
31260         if(this.el){
31261             this.el.child("button:first").focus();
31262         }
31263     },
31264
31265     // private
31266     onClick : function(e){
31267         e.preventDefault();
31268         if(!this.disabled){
31269             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31270                 if(this.menu && !this.menu.isVisible()){
31271                     this.menu.show(this.el, this.menuAlign);
31272                 }
31273                 this.fireEvent("arrowclick", this, e);
31274                 if(this.arrowHandler){
31275                     this.arrowHandler.call(this.scope || this, this, e);
31276                 }
31277             }else{
31278                 this.fireEvent("click", this, e);
31279                 if(this.handler){
31280                     this.handler.call(this.scope || this, this, e);
31281                 }
31282             }
31283         }
31284     },
31285     // private
31286     onMouseDown : function(e){
31287         if(!this.disabled){
31288             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31289         }
31290     },
31291     // private
31292     onMouseUp : function(e){
31293         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31294     }   
31295 });
31296
31297
31298 // backwards compat
31299 Roo.MenuButton = Roo.SplitButton;/*
31300  * Based on:
31301  * Ext JS Library 1.1.1
31302  * Copyright(c) 2006-2007, Ext JS, LLC.
31303  *
31304  * Originally Released Under LGPL - original licence link has changed is not relivant.
31305  *
31306  * Fork - LGPL
31307  * <script type="text/javascript">
31308  */
31309
31310 /**
31311  * @class Roo.Toolbar
31312  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31313  * Basic Toolbar class.
31314  * @constructor
31315  * Creates a new Toolbar
31316  * @param {Object} container The config object
31317  */ 
31318 Roo.Toolbar = function(container, buttons, config)
31319 {
31320     /// old consturctor format still supported..
31321     if(container instanceof Array){ // omit the container for later rendering
31322         buttons = container;
31323         config = buttons;
31324         container = null;
31325     }
31326     if (typeof(container) == 'object' && container.xtype) {
31327         config = container;
31328         container = config.container;
31329         buttons = config.buttons || []; // not really - use items!!
31330     }
31331     var xitems = [];
31332     if (config && config.items) {
31333         xitems = config.items;
31334         delete config.items;
31335     }
31336     Roo.apply(this, config);
31337     this.buttons = buttons;
31338     
31339     if(container){
31340         this.render(container);
31341     }
31342     this.xitems = xitems;
31343     Roo.each(xitems, function(b) {
31344         this.add(b);
31345     }, this);
31346     
31347 };
31348
31349 Roo.Toolbar.prototype = {
31350     /**
31351      * @cfg {Array} items
31352      * array of button configs or elements to add (will be converted to a MixedCollection)
31353      */
31354     items: false,
31355     /**
31356      * @cfg {String/HTMLElement/Element} container
31357      * The id or element that will contain the toolbar
31358      */
31359     // private
31360     render : function(ct){
31361         this.el = Roo.get(ct);
31362         if(this.cls){
31363             this.el.addClass(this.cls);
31364         }
31365         // using a table allows for vertical alignment
31366         // 100% width is needed by Safari...
31367         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31368         this.tr = this.el.child("tr", true);
31369         var autoId = 0;
31370         this.items = new Roo.util.MixedCollection(false, function(o){
31371             return o.id || ("item" + (++autoId));
31372         });
31373         if(this.buttons){
31374             this.add.apply(this, this.buttons);
31375             delete this.buttons;
31376         }
31377     },
31378
31379     /**
31380      * Adds element(s) to the toolbar -- this function takes a variable number of 
31381      * arguments of mixed type and adds them to the toolbar.
31382      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31383      * <ul>
31384      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31385      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31386      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31387      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31388      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31389      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31390      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31391      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31392      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31393      * </ul>
31394      * @param {Mixed} arg2
31395      * @param {Mixed} etc.
31396      */
31397     add : function(){
31398         var a = arguments, l = a.length;
31399         for(var i = 0; i < l; i++){
31400             this._add(a[i]);
31401         }
31402     },
31403     // private..
31404     _add : function(el) {
31405         
31406         if (el.xtype) {
31407             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31408         }
31409         
31410         if (el.applyTo){ // some kind of form field
31411             return this.addField(el);
31412         } 
31413         if (el.render){ // some kind of Toolbar.Item
31414             return this.addItem(el);
31415         }
31416         if (typeof el == "string"){ // string
31417             if(el == "separator" || el == "-"){
31418                 return this.addSeparator();
31419             }
31420             if (el == " "){
31421                 return this.addSpacer();
31422             }
31423             if(el == "->"){
31424                 return this.addFill();
31425             }
31426             return this.addText(el);
31427             
31428         }
31429         if(el.tagName){ // element
31430             return this.addElement(el);
31431         }
31432         if(typeof el == "object"){ // must be button config?
31433             return this.addButton(el);
31434         }
31435         // and now what?!?!
31436         return false;
31437         
31438     },
31439     
31440     /**
31441      * Add an Xtype element
31442      * @param {Object} xtype Xtype Object
31443      * @return {Object} created Object
31444      */
31445     addxtype : function(e){
31446         return this.add(e);  
31447     },
31448     
31449     /**
31450      * Returns the Element for this toolbar.
31451      * @return {Roo.Element}
31452      */
31453     getEl : function(){
31454         return this.el;  
31455     },
31456     
31457     /**
31458      * Adds a separator
31459      * @return {Roo.Toolbar.Item} The separator item
31460      */
31461     addSeparator : function(){
31462         return this.addItem(new Roo.Toolbar.Separator());
31463     },
31464
31465     /**
31466      * Adds a spacer element
31467      * @return {Roo.Toolbar.Spacer} The spacer item
31468      */
31469     addSpacer : function(){
31470         return this.addItem(new Roo.Toolbar.Spacer());
31471     },
31472
31473     /**
31474      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31475      * @return {Roo.Toolbar.Fill} The fill item
31476      */
31477     addFill : function(){
31478         return this.addItem(new Roo.Toolbar.Fill());
31479     },
31480
31481     /**
31482      * Adds any standard HTML element to the toolbar
31483      * @param {String/HTMLElement/Element} el The element or id of the element to add
31484      * @return {Roo.Toolbar.Item} The element's item
31485      */
31486     addElement : function(el){
31487         return this.addItem(new Roo.Toolbar.Item(el));
31488     },
31489     /**
31490      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31491      * @type Roo.util.MixedCollection  
31492      */
31493     items : false,
31494      
31495     /**
31496      * Adds any Toolbar.Item or subclass
31497      * @param {Roo.Toolbar.Item} item
31498      * @return {Roo.Toolbar.Item} The item
31499      */
31500     addItem : function(item){
31501         var td = this.nextBlock();
31502         item.render(td);
31503         this.items.add(item);
31504         return item;
31505     },
31506     
31507     /**
31508      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31509      * @param {Object/Array} config A button config or array of configs
31510      * @return {Roo.Toolbar.Button/Array}
31511      */
31512     addButton : function(config){
31513         if(config instanceof Array){
31514             var buttons = [];
31515             for(var i = 0, len = config.length; i < len; i++) {
31516                 buttons.push(this.addButton(config[i]));
31517             }
31518             return buttons;
31519         }
31520         var b = config;
31521         if(!(config instanceof Roo.Toolbar.Button)){
31522             b = config.split ?
31523                 new Roo.Toolbar.SplitButton(config) :
31524                 new Roo.Toolbar.Button(config);
31525         }
31526         var td = this.nextBlock();
31527         b.render(td);
31528         this.items.add(b);
31529         return b;
31530     },
31531     
31532     /**
31533      * Adds text to the toolbar
31534      * @param {String} text The text to add
31535      * @return {Roo.Toolbar.Item} The element's item
31536      */
31537     addText : function(text){
31538         return this.addItem(new Roo.Toolbar.TextItem(text));
31539     },
31540     
31541     /**
31542      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31543      * @param {Number} index The index where the item is to be inserted
31544      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31545      * @return {Roo.Toolbar.Button/Item}
31546      */
31547     insertButton : function(index, item){
31548         if(item instanceof Array){
31549             var buttons = [];
31550             for(var i = 0, len = item.length; i < len; i++) {
31551                buttons.push(this.insertButton(index + i, item[i]));
31552             }
31553             return buttons;
31554         }
31555         if (!(item instanceof Roo.Toolbar.Button)){
31556            item = new Roo.Toolbar.Button(item);
31557         }
31558         var td = document.createElement("td");
31559         this.tr.insertBefore(td, this.tr.childNodes[index]);
31560         item.render(td);
31561         this.items.insert(index, item);
31562         return item;
31563     },
31564     
31565     /**
31566      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31567      * @param {Object} config
31568      * @return {Roo.Toolbar.Item} The element's item
31569      */
31570     addDom : function(config, returnEl){
31571         var td = this.nextBlock();
31572         Roo.DomHelper.overwrite(td, config);
31573         var ti = new Roo.Toolbar.Item(td.firstChild);
31574         ti.render(td);
31575         this.items.add(ti);
31576         return ti;
31577     },
31578
31579     /**
31580      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31581      * @type Roo.util.MixedCollection  
31582      */
31583     fields : false,
31584     
31585     /**
31586      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31587      * Note: the field should not have been rendered yet. For a field that has already been
31588      * rendered, use {@link #addElement}.
31589      * @param {Roo.form.Field} field
31590      * @return {Roo.ToolbarItem}
31591      */
31592      
31593       
31594     addField : function(field) {
31595         if (!this.fields) {
31596             var autoId = 0;
31597             this.fields = new Roo.util.MixedCollection(false, function(o){
31598                 return o.id || ("item" + (++autoId));
31599             });
31600
31601         }
31602         
31603         var td = this.nextBlock();
31604         field.render(td);
31605         var ti = new Roo.Toolbar.Item(td.firstChild);
31606         ti.render(td);
31607         this.items.add(ti);
31608         this.fields.add(field);
31609         return ti;
31610     },
31611     /**
31612      * Hide the toolbar
31613      * @method hide
31614      */
31615      
31616       
31617     hide : function()
31618     {
31619         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31620         this.el.child('div').hide();
31621     },
31622     /**
31623      * Show the toolbar
31624      * @method show
31625      */
31626     show : function()
31627     {
31628         this.el.child('div').show();
31629     },
31630       
31631     // private
31632     nextBlock : function(){
31633         var td = document.createElement("td");
31634         this.tr.appendChild(td);
31635         return td;
31636     },
31637
31638     // private
31639     destroy : function(){
31640         if(this.items){ // rendered?
31641             Roo.destroy.apply(Roo, this.items.items);
31642         }
31643         if(this.fields){ // rendered?
31644             Roo.destroy.apply(Roo, this.fields.items);
31645         }
31646         Roo.Element.uncache(this.el, this.tr);
31647     }
31648 };
31649
31650 /**
31651  * @class Roo.Toolbar.Item
31652  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31653  * @constructor
31654  * Creates a new Item
31655  * @param {HTMLElement} el 
31656  */
31657 Roo.Toolbar.Item = function(el){
31658     var cfg = {};
31659     if (typeof (el.xtype) != 'undefined') {
31660         cfg = el;
31661         el = cfg.el;
31662     }
31663     
31664     this.el = Roo.getDom(el);
31665     this.id = Roo.id(this.el);
31666     this.hidden = false;
31667     
31668     this.addEvents({
31669          /**
31670              * @event render
31671              * Fires when the button is rendered
31672              * @param {Button} this
31673              */
31674         'render': true
31675     });
31676     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31677 };
31678 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31679 //Roo.Toolbar.Item.prototype = {
31680     
31681     /**
31682      * Get this item's HTML Element
31683      * @return {HTMLElement}
31684      */
31685     getEl : function(){
31686        return this.el;  
31687     },
31688
31689     // private
31690     render : function(td){
31691         
31692          this.td = td;
31693         td.appendChild(this.el);
31694         
31695         this.fireEvent('render', this);
31696     },
31697     
31698     /**
31699      * Removes and destroys this item.
31700      */
31701     destroy : function(){
31702         this.td.parentNode.removeChild(this.td);
31703     },
31704     
31705     /**
31706      * Shows this item.
31707      */
31708     show: function(){
31709         this.hidden = false;
31710         this.td.style.display = "";
31711     },
31712     
31713     /**
31714      * Hides this item.
31715      */
31716     hide: function(){
31717         this.hidden = true;
31718         this.td.style.display = "none";
31719     },
31720     
31721     /**
31722      * Convenience function for boolean show/hide.
31723      * @param {Boolean} visible true to show/false to hide
31724      */
31725     setVisible: function(visible){
31726         if(visible) {
31727             this.show();
31728         }else{
31729             this.hide();
31730         }
31731     },
31732     
31733     /**
31734      * Try to focus this item.
31735      */
31736     focus : function(){
31737         Roo.fly(this.el).focus();
31738     },
31739     
31740     /**
31741      * Disables this item.
31742      */
31743     disable : function(){
31744         Roo.fly(this.td).addClass("x-item-disabled");
31745         this.disabled = true;
31746         this.el.disabled = true;
31747     },
31748     
31749     /**
31750      * Enables this item.
31751      */
31752     enable : function(){
31753         Roo.fly(this.td).removeClass("x-item-disabled");
31754         this.disabled = false;
31755         this.el.disabled = false;
31756     }
31757 });
31758
31759
31760 /**
31761  * @class Roo.Toolbar.Separator
31762  * @extends Roo.Toolbar.Item
31763  * A simple toolbar separator class
31764  * @constructor
31765  * Creates a new Separator
31766  */
31767 Roo.Toolbar.Separator = function(cfg){
31768     
31769     var s = document.createElement("span");
31770     s.className = "ytb-sep";
31771     if (cfg) {
31772         cfg.el = s;
31773     }
31774     
31775     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31776 };
31777 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31778     enable:Roo.emptyFn,
31779     disable:Roo.emptyFn,
31780     focus:Roo.emptyFn
31781 });
31782
31783 /**
31784  * @class Roo.Toolbar.Spacer
31785  * @extends Roo.Toolbar.Item
31786  * A simple element that adds extra horizontal space to a toolbar.
31787  * @constructor
31788  * Creates a new Spacer
31789  */
31790 Roo.Toolbar.Spacer = function(cfg){
31791     var s = document.createElement("div");
31792     s.className = "ytb-spacer";
31793     if (cfg) {
31794         cfg.el = s;
31795     }
31796     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31797 };
31798 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31799     enable:Roo.emptyFn,
31800     disable:Roo.emptyFn,
31801     focus:Roo.emptyFn
31802 });
31803
31804 /**
31805  * @class Roo.Toolbar.Fill
31806  * @extends Roo.Toolbar.Spacer
31807  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31808  * @constructor
31809  * Creates a new Spacer
31810  */
31811 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31812     // private
31813     render : function(td){
31814         td.style.width = '100%';
31815         Roo.Toolbar.Fill.superclass.render.call(this, td);
31816     }
31817 });
31818
31819 /**
31820  * @class Roo.Toolbar.TextItem
31821  * @extends Roo.Toolbar.Item
31822  * A simple class that renders text directly into a toolbar.
31823  * @constructor
31824  * Creates a new TextItem
31825  * @cfg {string} text 
31826  */
31827 Roo.Toolbar.TextItem = function(cfg){
31828     var  text = cfg || "";
31829     if (typeof(cfg) == 'object') {
31830         text = cfg.text || "";
31831     }  else {
31832         cfg = null;
31833     }
31834     var s = document.createElement("span");
31835     s.className = "ytb-text";
31836     s.innerHTML = text;
31837     if (cfg) {
31838         cfg.el  = s;
31839     }
31840     
31841     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31842 };
31843 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31844     
31845      
31846     enable:Roo.emptyFn,
31847     disable:Roo.emptyFn,
31848     focus:Roo.emptyFn,
31849      /**
31850      * Shows this button
31851      */
31852     show: function(){
31853         this.hidden = false;
31854         this.el.style.display = "";
31855     },
31856     
31857     /**
31858      * Hides this button
31859      */
31860     hide: function(){
31861         this.hidden = true;
31862         this.el.style.display = "none";
31863     }
31864     
31865 });
31866
31867 /**
31868  * @class Roo.Toolbar.Button
31869  * @extends Roo.Button
31870  * A button that renders into a toolbar.
31871  * @constructor
31872  * Creates a new Button
31873  * @param {Object} config A standard {@link Roo.Button} config object
31874  */
31875 Roo.Toolbar.Button = function(config){
31876     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31877 };
31878 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31879 {
31880     
31881     
31882     render : function(td){
31883         this.td = td;
31884         Roo.Toolbar.Button.superclass.render.call(this, td);
31885     },
31886     
31887     /**
31888      * Removes and destroys this button
31889      */
31890     destroy : function(){
31891         Roo.Toolbar.Button.superclass.destroy.call(this);
31892         this.td.parentNode.removeChild(this.td);
31893     },
31894     
31895     /**
31896      * Shows this button
31897      */
31898     show: function(){
31899         this.hidden = false;
31900         this.td.style.display = "";
31901     },
31902     
31903     /**
31904      * Hides this button
31905      */
31906     hide: function(){
31907         this.hidden = true;
31908         this.td.style.display = "none";
31909     },
31910
31911     /**
31912      * Disables this item
31913      */
31914     disable : function(){
31915         Roo.fly(this.td).addClass("x-item-disabled");
31916         this.disabled = true;
31917     },
31918
31919     /**
31920      * Enables this item
31921      */
31922     enable : function(){
31923         Roo.fly(this.td).removeClass("x-item-disabled");
31924         this.disabled = false;
31925     }
31926 });
31927 // backwards compat
31928 Roo.ToolbarButton = Roo.Toolbar.Button;
31929
31930 /**
31931  * @class Roo.Toolbar.SplitButton
31932  * @extends Roo.SplitButton
31933  * A menu button that renders into a toolbar.
31934  * @constructor
31935  * Creates a new SplitButton
31936  * @param {Object} config A standard {@link Roo.SplitButton} config object
31937  */
31938 Roo.Toolbar.SplitButton = function(config){
31939     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31940 };
31941 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31942     render : function(td){
31943         this.td = td;
31944         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31945     },
31946     
31947     /**
31948      * Removes and destroys this button
31949      */
31950     destroy : function(){
31951         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31952         this.td.parentNode.removeChild(this.td);
31953     },
31954     
31955     /**
31956      * Shows this button
31957      */
31958     show: function(){
31959         this.hidden = false;
31960         this.td.style.display = "";
31961     },
31962     
31963     /**
31964      * Hides this button
31965      */
31966     hide: function(){
31967         this.hidden = true;
31968         this.td.style.display = "none";
31969     }
31970 });
31971
31972 // backwards compat
31973 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31974  * Based on:
31975  * Ext JS Library 1.1.1
31976  * Copyright(c) 2006-2007, Ext JS, LLC.
31977  *
31978  * Originally Released Under LGPL - original licence link has changed is not relivant.
31979  *
31980  * Fork - LGPL
31981  * <script type="text/javascript">
31982  */
31983  
31984 /**
31985  * @class Roo.PagingToolbar
31986  * @extends Roo.Toolbar
31987  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31988  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31989  * @constructor
31990  * Create a new PagingToolbar
31991  * @param {Object} config The config object
31992  */
31993 Roo.PagingToolbar = function(el, ds, config)
31994 {
31995     // old args format still supported... - xtype is prefered..
31996     if (typeof(el) == 'object' && el.xtype) {
31997         // created from xtype...
31998         config = el;
31999         ds = el.dataSource;
32000         el = config.container;
32001     }
32002     var items = [];
32003     if (config.items) {
32004         items = config.items;
32005         config.items = [];
32006     }
32007     
32008     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32009     this.ds = ds;
32010     this.cursor = 0;
32011     this.renderButtons(this.el);
32012     this.bind(ds);
32013     
32014     // supprot items array.
32015    
32016     Roo.each(items, function(e) {
32017         this.add(Roo.factory(e));
32018     },this);
32019     
32020 };
32021
32022 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32023    
32024     /**
32025      * @cfg {String/HTMLElement/Element} container
32026      * container The id or element that will contain the toolbar
32027      */
32028     /**
32029      * @cfg {Boolean} displayInfo
32030      * True to display the displayMsg (defaults to false)
32031      */
32032     
32033     
32034     /**
32035      * @cfg {Number} pageSize
32036      * The number of records to display per page (defaults to 20)
32037      */
32038     pageSize: 20,
32039     /**
32040      * @cfg {String} displayMsg
32041      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32042      */
32043     displayMsg : 'Displaying {0} - {1} of {2}',
32044     /**
32045      * @cfg {String} emptyMsg
32046      * The message to display when no records are found (defaults to "No data to display")
32047      */
32048     emptyMsg : 'No data to display',
32049     /**
32050      * Customizable piece of the default paging text (defaults to "Page")
32051      * @type String
32052      */
32053     beforePageText : "Page",
32054     /**
32055      * Customizable piece of the default paging text (defaults to "of %0")
32056      * @type String
32057      */
32058     afterPageText : "of {0}",
32059     /**
32060      * Customizable piece of the default paging text (defaults to "First Page")
32061      * @type String
32062      */
32063     firstText : "First Page",
32064     /**
32065      * Customizable piece of the default paging text (defaults to "Previous Page")
32066      * @type String
32067      */
32068     prevText : "Previous Page",
32069     /**
32070      * Customizable piece of the default paging text (defaults to "Next Page")
32071      * @type String
32072      */
32073     nextText : "Next Page",
32074     /**
32075      * Customizable piece of the default paging text (defaults to "Last Page")
32076      * @type String
32077      */
32078     lastText : "Last Page",
32079     /**
32080      * Customizable piece of the default paging text (defaults to "Refresh")
32081      * @type String
32082      */
32083     refreshText : "Refresh",
32084
32085     // private
32086     renderButtons : function(el){
32087         Roo.PagingToolbar.superclass.render.call(this, el);
32088         this.first = this.addButton({
32089             tooltip: this.firstText,
32090             cls: "x-btn-icon x-grid-page-first",
32091             disabled: true,
32092             handler: this.onClick.createDelegate(this, ["first"])
32093         });
32094         this.prev = this.addButton({
32095             tooltip: this.prevText,
32096             cls: "x-btn-icon x-grid-page-prev",
32097             disabled: true,
32098             handler: this.onClick.createDelegate(this, ["prev"])
32099         });
32100         //this.addSeparator();
32101         this.add(this.beforePageText);
32102         this.field = Roo.get(this.addDom({
32103            tag: "input",
32104            type: "text",
32105            size: "3",
32106            value: "1",
32107            cls: "x-grid-page-number"
32108         }).el);
32109         this.field.on("keydown", this.onPagingKeydown, this);
32110         this.field.on("focus", function(){this.dom.select();});
32111         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32112         this.field.setHeight(18);
32113         //this.addSeparator();
32114         this.next = this.addButton({
32115             tooltip: this.nextText,
32116             cls: "x-btn-icon x-grid-page-next",
32117             disabled: true,
32118             handler: this.onClick.createDelegate(this, ["next"])
32119         });
32120         this.last = this.addButton({
32121             tooltip: this.lastText,
32122             cls: "x-btn-icon x-grid-page-last",
32123             disabled: true,
32124             handler: this.onClick.createDelegate(this, ["last"])
32125         });
32126         //this.addSeparator();
32127         this.loading = this.addButton({
32128             tooltip: this.refreshText,
32129             cls: "x-btn-icon x-grid-loading",
32130             handler: this.onClick.createDelegate(this, ["refresh"])
32131         });
32132
32133         if(this.displayInfo){
32134             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32135         }
32136     },
32137
32138     // private
32139     updateInfo : function(){
32140         if(this.displayEl){
32141             var count = this.ds.getCount();
32142             var msg = count == 0 ?
32143                 this.emptyMsg :
32144                 String.format(
32145                     this.displayMsg,
32146                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32147                 );
32148             this.displayEl.update(msg);
32149         }
32150     },
32151
32152     // private
32153     onLoad : function(ds, r, o){
32154        this.cursor = o.params ? o.params.start : 0;
32155        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32156
32157        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32158        this.field.dom.value = ap;
32159        this.first.setDisabled(ap == 1);
32160        this.prev.setDisabled(ap == 1);
32161        this.next.setDisabled(ap == ps);
32162        this.last.setDisabled(ap == ps);
32163        this.loading.enable();
32164        this.updateInfo();
32165     },
32166
32167     // private
32168     getPageData : function(){
32169         var total = this.ds.getTotalCount();
32170         return {
32171             total : total,
32172             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32173             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32174         };
32175     },
32176
32177     // private
32178     onLoadError : function(){
32179         this.loading.enable();
32180     },
32181
32182     // private
32183     onPagingKeydown : function(e){
32184         var k = e.getKey();
32185         var d = this.getPageData();
32186         if(k == e.RETURN){
32187             var v = this.field.dom.value, pageNum;
32188             if(!v || isNaN(pageNum = parseInt(v, 10))){
32189                 this.field.dom.value = d.activePage;
32190                 return;
32191             }
32192             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32193             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32194             e.stopEvent();
32195         }
32196         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))
32197         {
32198           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32199           this.field.dom.value = pageNum;
32200           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32201           e.stopEvent();
32202         }
32203         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32204         {
32205           var v = this.field.dom.value, pageNum; 
32206           var increment = (e.shiftKey) ? 10 : 1;
32207           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32208             increment *= -1;
32209           }
32210           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32211             this.field.dom.value = d.activePage;
32212             return;
32213           }
32214           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32215           {
32216             this.field.dom.value = parseInt(v, 10) + increment;
32217             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32218             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32219           }
32220           e.stopEvent();
32221         }
32222     },
32223
32224     // private
32225     beforeLoad : function(){
32226         if(this.loading){
32227             this.loading.disable();
32228         }
32229     },
32230
32231     // private
32232     onClick : function(which){
32233         var ds = this.ds;
32234         switch(which){
32235             case "first":
32236                 ds.load({params:{start: 0, limit: this.pageSize}});
32237             break;
32238             case "prev":
32239                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32240             break;
32241             case "next":
32242                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32243             break;
32244             case "last":
32245                 var total = ds.getTotalCount();
32246                 var extra = total % this.pageSize;
32247                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32248                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32249             break;
32250             case "refresh":
32251                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32252             break;
32253         }
32254     },
32255
32256     /**
32257      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32258      * @param {Roo.data.Store} store The data store to unbind
32259      */
32260     unbind : function(ds){
32261         ds.un("beforeload", this.beforeLoad, this);
32262         ds.un("load", this.onLoad, this);
32263         ds.un("loadexception", this.onLoadError, this);
32264         ds.un("remove", this.updateInfo, this);
32265         ds.un("add", this.updateInfo, this);
32266         this.ds = undefined;
32267     },
32268
32269     /**
32270      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32271      * @param {Roo.data.Store} store The data store to bind
32272      */
32273     bind : function(ds){
32274         ds.on("beforeload", this.beforeLoad, this);
32275         ds.on("load", this.onLoad, this);
32276         ds.on("loadexception", this.onLoadError, this);
32277         ds.on("remove", this.updateInfo, this);
32278         ds.on("add", this.updateInfo, this);
32279         this.ds = ds;
32280     }
32281 });/*
32282  * Based on:
32283  * Ext JS Library 1.1.1
32284  * Copyright(c) 2006-2007, Ext JS, LLC.
32285  *
32286  * Originally Released Under LGPL - original licence link has changed is not relivant.
32287  *
32288  * Fork - LGPL
32289  * <script type="text/javascript">
32290  */
32291
32292 /**
32293  * @class Roo.Resizable
32294  * @extends Roo.util.Observable
32295  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32296  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32297  * 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
32298  * the element will be wrapped for you automatically.</p>
32299  * <p>Here is the list of valid resize handles:</p>
32300  * <pre>
32301 Value   Description
32302 ------  -------------------
32303  'n'     north
32304  's'     south
32305  'e'     east
32306  'w'     west
32307  'nw'    northwest
32308  'sw'    southwest
32309  'se'    southeast
32310  'ne'    northeast
32311  'hd'    horizontal drag
32312  'all'   all
32313 </pre>
32314  * <p>Here's an example showing the creation of a typical Resizable:</p>
32315  * <pre><code>
32316 var resizer = new Roo.Resizable("element-id", {
32317     handles: 'all',
32318     minWidth: 200,
32319     minHeight: 100,
32320     maxWidth: 500,
32321     maxHeight: 400,
32322     pinned: true
32323 });
32324 resizer.on("resize", myHandler);
32325 </code></pre>
32326  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32327  * resizer.east.setDisplayed(false);</p>
32328  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32329  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32330  * resize operation's new size (defaults to [0, 0])
32331  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32332  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32333  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32334  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32335  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32336  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32337  * @cfg {Number} width The width of the element in pixels (defaults to null)
32338  * @cfg {Number} height The height of the element in pixels (defaults to null)
32339  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32340  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32341  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32342  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32343  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32344  * in favor of the handles config option (defaults to false)
32345  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32346  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32347  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32348  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32349  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32350  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32351  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32352  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32353  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32354  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32355  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32356  * @constructor
32357  * Create a new resizable component
32358  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32359  * @param {Object} config configuration options
32360   */
32361 Roo.Resizable = function(el, config)
32362 {
32363     this.el = Roo.get(el);
32364
32365     if(config && config.wrap){
32366         config.resizeChild = this.el;
32367         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32368         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32369         this.el.setStyle("overflow", "hidden");
32370         this.el.setPositioning(config.resizeChild.getPositioning());
32371         config.resizeChild.clearPositioning();
32372         if(!config.width || !config.height){
32373             var csize = config.resizeChild.getSize();
32374             this.el.setSize(csize.width, csize.height);
32375         }
32376         if(config.pinned && !config.adjustments){
32377             config.adjustments = "auto";
32378         }
32379     }
32380
32381     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32382     this.proxy.unselectable();
32383     this.proxy.enableDisplayMode('block');
32384
32385     Roo.apply(this, config);
32386
32387     if(this.pinned){
32388         this.disableTrackOver = true;
32389         this.el.addClass("x-resizable-pinned");
32390     }
32391     // if the element isn't positioned, make it relative
32392     var position = this.el.getStyle("position");
32393     if(position != "absolute" && position != "fixed"){
32394         this.el.setStyle("position", "relative");
32395     }
32396     if(!this.handles){ // no handles passed, must be legacy style
32397         this.handles = 's,e,se';
32398         if(this.multiDirectional){
32399             this.handles += ',n,w';
32400         }
32401     }
32402     if(this.handles == "all"){
32403         this.handles = "n s e w ne nw se sw";
32404     }
32405     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32406     var ps = Roo.Resizable.positions;
32407     for(var i = 0, len = hs.length; i < len; i++){
32408         if(hs[i] && ps[hs[i]]){
32409             var pos = ps[hs[i]];
32410             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32411         }
32412     }
32413     // legacy
32414     this.corner = this.southeast;
32415     
32416     // updateBox = the box can move..
32417     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32418         this.updateBox = true;
32419     }
32420
32421     this.activeHandle = null;
32422
32423     if(this.resizeChild){
32424         if(typeof this.resizeChild == "boolean"){
32425             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32426         }else{
32427             this.resizeChild = Roo.get(this.resizeChild, true);
32428         }
32429     }
32430     
32431     if(this.adjustments == "auto"){
32432         var rc = this.resizeChild;
32433         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32434         if(rc && (hw || hn)){
32435             rc.position("relative");
32436             rc.setLeft(hw ? hw.el.getWidth() : 0);
32437             rc.setTop(hn ? hn.el.getHeight() : 0);
32438         }
32439         this.adjustments = [
32440             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32441             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32442         ];
32443     }
32444
32445     if(this.draggable){
32446         this.dd = this.dynamic ?
32447             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32448         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32449     }
32450
32451     // public events
32452     this.addEvents({
32453         /**
32454          * @event beforeresize
32455          * Fired before resize is allowed. Set enabled to false to cancel resize.
32456          * @param {Roo.Resizable} this
32457          * @param {Roo.EventObject} e The mousedown event
32458          */
32459         "beforeresize" : true,
32460         /**
32461          * @event resizing
32462          * Fired a resizing.
32463          * @param {Roo.Resizable} this
32464          * @param {Number} x The new x position
32465          * @param {Number} y The new y position
32466          * @param {Number} w The new w width
32467          * @param {Number} h The new h hight
32468          * @param {Roo.EventObject} e The mouseup event
32469          */
32470         "resizing" : true,
32471         /**
32472          * @event resize
32473          * Fired after a resize.
32474          * @param {Roo.Resizable} this
32475          * @param {Number} width The new width
32476          * @param {Number} height The new height
32477          * @param {Roo.EventObject} e The mouseup event
32478          */
32479         "resize" : true
32480     });
32481
32482     if(this.width !== null && this.height !== null){
32483         this.resizeTo(this.width, this.height);
32484     }else{
32485         this.updateChildSize();
32486     }
32487     if(Roo.isIE){
32488         this.el.dom.style.zoom = 1;
32489     }
32490     Roo.Resizable.superclass.constructor.call(this);
32491 };
32492
32493 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32494         resizeChild : false,
32495         adjustments : [0, 0],
32496         minWidth : 5,
32497         minHeight : 5,
32498         maxWidth : 10000,
32499         maxHeight : 10000,
32500         enabled : true,
32501         animate : false,
32502         duration : .35,
32503         dynamic : false,
32504         handles : false,
32505         multiDirectional : false,
32506         disableTrackOver : false,
32507         easing : 'easeOutStrong',
32508         widthIncrement : 0,
32509         heightIncrement : 0,
32510         pinned : false,
32511         width : null,
32512         height : null,
32513         preserveRatio : false,
32514         transparent: false,
32515         minX: 0,
32516         minY: 0,
32517         draggable: false,
32518
32519         /**
32520          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32521          */
32522         constrainTo: undefined,
32523         /**
32524          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32525          */
32526         resizeRegion: undefined,
32527
32528
32529     /**
32530      * Perform a manual resize
32531      * @param {Number} width
32532      * @param {Number} height
32533      */
32534     resizeTo : function(width, height){
32535         this.el.setSize(width, height);
32536         this.updateChildSize();
32537         this.fireEvent("resize", this, width, height, null);
32538     },
32539
32540     // private
32541     startSizing : function(e, handle){
32542         this.fireEvent("beforeresize", this, e);
32543         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32544
32545             if(!this.overlay){
32546                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32547                 this.overlay.unselectable();
32548                 this.overlay.enableDisplayMode("block");
32549                 this.overlay.on("mousemove", this.onMouseMove, this);
32550                 this.overlay.on("mouseup", this.onMouseUp, this);
32551             }
32552             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32553
32554             this.resizing = true;
32555             this.startBox = this.el.getBox();
32556             this.startPoint = e.getXY();
32557             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32558                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32559
32560             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32561             this.overlay.show();
32562
32563             if(this.constrainTo) {
32564                 var ct = Roo.get(this.constrainTo);
32565                 this.resizeRegion = ct.getRegion().adjust(
32566                     ct.getFrameWidth('t'),
32567                     ct.getFrameWidth('l'),
32568                     -ct.getFrameWidth('b'),
32569                     -ct.getFrameWidth('r')
32570                 );
32571             }
32572
32573             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32574             this.proxy.show();
32575             this.proxy.setBox(this.startBox);
32576             if(!this.dynamic){
32577                 this.proxy.setStyle('visibility', 'visible');
32578             }
32579         }
32580     },
32581
32582     // private
32583     onMouseDown : function(handle, e){
32584         if(this.enabled){
32585             e.stopEvent();
32586             this.activeHandle = handle;
32587             this.startSizing(e, handle);
32588         }
32589     },
32590
32591     // private
32592     onMouseUp : function(e){
32593         var size = this.resizeElement();
32594         this.resizing = false;
32595         this.handleOut();
32596         this.overlay.hide();
32597         this.proxy.hide();
32598         this.fireEvent("resize", this, size.width, size.height, e);
32599     },
32600
32601     // private
32602     updateChildSize : function(){
32603         
32604         if(this.resizeChild){
32605             var el = this.el;
32606             var child = this.resizeChild;
32607             var adj = this.adjustments;
32608             if(el.dom.offsetWidth){
32609                 var b = el.getSize(true);
32610                 child.setSize(b.width+adj[0], b.height+adj[1]);
32611             }
32612             // Second call here for IE
32613             // The first call enables instant resizing and
32614             // the second call corrects scroll bars if they
32615             // exist
32616             if(Roo.isIE){
32617                 setTimeout(function(){
32618                     if(el.dom.offsetWidth){
32619                         var b = el.getSize(true);
32620                         child.setSize(b.width+adj[0], b.height+adj[1]);
32621                     }
32622                 }, 10);
32623             }
32624         }
32625     },
32626
32627     // private
32628     snap : function(value, inc, min){
32629         if(!inc || !value) {
32630             return value;
32631         }
32632         var newValue = value;
32633         var m = value % inc;
32634         if(m > 0){
32635             if(m > (inc/2)){
32636                 newValue = value + (inc-m);
32637             }else{
32638                 newValue = value - m;
32639             }
32640         }
32641         return Math.max(min, newValue);
32642     },
32643
32644     // private
32645     resizeElement : function(){
32646         var box = this.proxy.getBox();
32647         if(this.updateBox){
32648             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32649         }else{
32650             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32651         }
32652         this.updateChildSize();
32653         if(!this.dynamic){
32654             this.proxy.hide();
32655         }
32656         return box;
32657     },
32658
32659     // private
32660     constrain : function(v, diff, m, mx){
32661         if(v - diff < m){
32662             diff = v - m;
32663         }else if(v - diff > mx){
32664             diff = mx - v;
32665         }
32666         return diff;
32667     },
32668
32669     // private
32670     onMouseMove : function(e){
32671         
32672         if(this.enabled){
32673             try{// try catch so if something goes wrong the user doesn't get hung
32674
32675             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32676                 return;
32677             }
32678
32679             //var curXY = this.startPoint;
32680             var curSize = this.curSize || this.startBox;
32681             var x = this.startBox.x, y = this.startBox.y;
32682             var ox = x, oy = y;
32683             var w = curSize.width, h = curSize.height;
32684             var ow = w, oh = h;
32685             var mw = this.minWidth, mh = this.minHeight;
32686             var mxw = this.maxWidth, mxh = this.maxHeight;
32687             var wi = this.widthIncrement;
32688             var hi = this.heightIncrement;
32689
32690             var eventXY = e.getXY();
32691             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32692             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32693
32694             var pos = this.activeHandle.position;
32695
32696             switch(pos){
32697                 case "east":
32698                     w += diffX;
32699                     w = Math.min(Math.max(mw, w), mxw);
32700                     break;
32701              
32702                 case "south":
32703                     h += diffY;
32704                     h = Math.min(Math.max(mh, h), mxh);
32705                     break;
32706                 case "southeast":
32707                     w += diffX;
32708                     h += diffY;
32709                     w = Math.min(Math.max(mw, w), mxw);
32710                     h = Math.min(Math.max(mh, h), mxh);
32711                     break;
32712                 case "north":
32713                     diffY = this.constrain(h, diffY, mh, mxh);
32714                     y += diffY;
32715                     h -= diffY;
32716                     break;
32717                 case "hdrag":
32718                     
32719                     if (wi) {
32720                         var adiffX = Math.abs(diffX);
32721                         var sub = (adiffX % wi); // how much 
32722                         if (sub > (wi/2)) { // far enough to snap
32723                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32724                         } else {
32725                             // remove difference.. 
32726                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32727                         }
32728                     }
32729                     x += diffX;
32730                     x = Math.max(this.minX, x);
32731                     break;
32732                 case "west":
32733                     diffX = this.constrain(w, diffX, mw, mxw);
32734                     x += diffX;
32735                     w -= diffX;
32736                     break;
32737                 case "northeast":
32738                     w += diffX;
32739                     w = Math.min(Math.max(mw, w), mxw);
32740                     diffY = this.constrain(h, diffY, mh, mxh);
32741                     y += diffY;
32742                     h -= diffY;
32743                     break;
32744                 case "northwest":
32745                     diffX = this.constrain(w, diffX, mw, mxw);
32746                     diffY = this.constrain(h, diffY, mh, mxh);
32747                     y += diffY;
32748                     h -= diffY;
32749                     x += diffX;
32750                     w -= diffX;
32751                     break;
32752                case "southwest":
32753                     diffX = this.constrain(w, diffX, mw, mxw);
32754                     h += diffY;
32755                     h = Math.min(Math.max(mh, h), mxh);
32756                     x += diffX;
32757                     w -= diffX;
32758                     break;
32759             }
32760
32761             var sw = this.snap(w, wi, mw);
32762             var sh = this.snap(h, hi, mh);
32763             if(sw != w || sh != h){
32764                 switch(pos){
32765                     case "northeast":
32766                         y -= sh - h;
32767                     break;
32768                     case "north":
32769                         y -= sh - h;
32770                         break;
32771                     case "southwest":
32772                         x -= sw - w;
32773                     break;
32774                     case "west":
32775                         x -= sw - w;
32776                         break;
32777                     case "northwest":
32778                         x -= sw - w;
32779                         y -= sh - h;
32780                     break;
32781                 }
32782                 w = sw;
32783                 h = sh;
32784             }
32785
32786             if(this.preserveRatio){
32787                 switch(pos){
32788                     case "southeast":
32789                     case "east":
32790                         h = oh * (w/ow);
32791                         h = Math.min(Math.max(mh, h), mxh);
32792                         w = ow * (h/oh);
32793                        break;
32794                     case "south":
32795                         w = ow * (h/oh);
32796                         w = Math.min(Math.max(mw, w), mxw);
32797                         h = oh * (w/ow);
32798                         break;
32799                     case "northeast":
32800                         w = ow * (h/oh);
32801                         w = Math.min(Math.max(mw, w), mxw);
32802                         h = oh * (w/ow);
32803                     break;
32804                     case "north":
32805                         var tw = w;
32806                         w = ow * (h/oh);
32807                         w = Math.min(Math.max(mw, w), mxw);
32808                         h = oh * (w/ow);
32809                         x += (tw - w) / 2;
32810                         break;
32811                     case "southwest":
32812                         h = oh * (w/ow);
32813                         h = Math.min(Math.max(mh, h), mxh);
32814                         var tw = w;
32815                         w = ow * (h/oh);
32816                         x += tw - w;
32817                         break;
32818                     case "west":
32819                         var th = h;
32820                         h = oh * (w/ow);
32821                         h = Math.min(Math.max(mh, h), mxh);
32822                         y += (th - h) / 2;
32823                         var tw = w;
32824                         w = ow * (h/oh);
32825                         x += tw - w;
32826                        break;
32827                     case "northwest":
32828                         var tw = w;
32829                         var th = h;
32830                         h = oh * (w/ow);
32831                         h = Math.min(Math.max(mh, h), mxh);
32832                         w = ow * (h/oh);
32833                         y += th - h;
32834                         x += tw - w;
32835                        break;
32836
32837                 }
32838             }
32839             if (pos == 'hdrag') {
32840                 w = ow;
32841             }
32842             this.proxy.setBounds(x, y, w, h);
32843             if(this.dynamic){
32844                 this.resizeElement();
32845             }
32846             }catch(e){}
32847         }
32848         this.fireEvent("resizing", this, x, y, w, h, e);
32849     },
32850
32851     // private
32852     handleOver : function(){
32853         if(this.enabled){
32854             this.el.addClass("x-resizable-over");
32855         }
32856     },
32857
32858     // private
32859     handleOut : function(){
32860         if(!this.resizing){
32861             this.el.removeClass("x-resizable-over");
32862         }
32863     },
32864
32865     /**
32866      * Returns the element this component is bound to.
32867      * @return {Roo.Element}
32868      */
32869     getEl : function(){
32870         return this.el;
32871     },
32872
32873     /**
32874      * Returns the resizeChild element (or null).
32875      * @return {Roo.Element}
32876      */
32877     getResizeChild : function(){
32878         return this.resizeChild;
32879     },
32880     groupHandler : function()
32881     {
32882         
32883     },
32884     /**
32885      * Destroys this resizable. If the element was wrapped and
32886      * removeEl is not true then the element remains.
32887      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32888      */
32889     destroy : function(removeEl){
32890         this.proxy.remove();
32891         if(this.overlay){
32892             this.overlay.removeAllListeners();
32893             this.overlay.remove();
32894         }
32895         var ps = Roo.Resizable.positions;
32896         for(var k in ps){
32897             if(typeof ps[k] != "function" && this[ps[k]]){
32898                 var h = this[ps[k]];
32899                 h.el.removeAllListeners();
32900                 h.el.remove();
32901             }
32902         }
32903         if(removeEl){
32904             this.el.update("");
32905             this.el.remove();
32906         }
32907     }
32908 });
32909
32910 // private
32911 // hash to map config positions to true positions
32912 Roo.Resizable.positions = {
32913     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32914     hd: "hdrag"
32915 };
32916
32917 // private
32918 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32919     if(!this.tpl){
32920         // only initialize the template if resizable is used
32921         var tpl = Roo.DomHelper.createTemplate(
32922             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32923         );
32924         tpl.compile();
32925         Roo.Resizable.Handle.prototype.tpl = tpl;
32926     }
32927     this.position = pos;
32928     this.rz = rz;
32929     // show north drag fro topdra
32930     var handlepos = pos == 'hdrag' ? 'north' : pos;
32931     
32932     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32933     if (pos == 'hdrag') {
32934         this.el.setStyle('cursor', 'pointer');
32935     }
32936     this.el.unselectable();
32937     if(transparent){
32938         this.el.setOpacity(0);
32939     }
32940     this.el.on("mousedown", this.onMouseDown, this);
32941     if(!disableTrackOver){
32942         this.el.on("mouseover", this.onMouseOver, this);
32943         this.el.on("mouseout", this.onMouseOut, this);
32944     }
32945 };
32946
32947 // private
32948 Roo.Resizable.Handle.prototype = {
32949     afterResize : function(rz){
32950         Roo.log('after?');
32951         // do nothing
32952     },
32953     // private
32954     onMouseDown : function(e){
32955         this.rz.onMouseDown(this, e);
32956     },
32957     // private
32958     onMouseOver : function(e){
32959         this.rz.handleOver(this, e);
32960     },
32961     // private
32962     onMouseOut : function(e){
32963         this.rz.handleOut(this, e);
32964     }
32965 };/*
32966  * Based on:
32967  * Ext JS Library 1.1.1
32968  * Copyright(c) 2006-2007, Ext JS, LLC.
32969  *
32970  * Originally Released Under LGPL - original licence link has changed is not relivant.
32971  *
32972  * Fork - LGPL
32973  * <script type="text/javascript">
32974  */
32975
32976 /**
32977  * @class Roo.Editor
32978  * @extends Roo.Component
32979  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32980  * @constructor
32981  * Create a new Editor
32982  * @param {Roo.form.Field} field The Field object (or descendant)
32983  * @param {Object} config The config object
32984  */
32985 Roo.Editor = function(field, config){
32986     Roo.Editor.superclass.constructor.call(this, config);
32987     this.field = field;
32988     this.addEvents({
32989         /**
32990              * @event beforestartedit
32991              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32992              * false from the handler of this event.
32993              * @param {Editor} this
32994              * @param {Roo.Element} boundEl The underlying element bound to this editor
32995              * @param {Mixed} value The field value being set
32996              */
32997         "beforestartedit" : true,
32998         /**
32999              * @event startedit
33000              * Fires when this editor is displayed
33001              * @param {Roo.Element} boundEl The underlying element bound to this editor
33002              * @param {Mixed} value The starting field value
33003              */
33004         "startedit" : true,
33005         /**
33006              * @event beforecomplete
33007              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33008              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33009              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33010              * event will not fire since no edit actually occurred.
33011              * @param {Editor} this
33012              * @param {Mixed} value The current field value
33013              * @param {Mixed} startValue The original field value
33014              */
33015         "beforecomplete" : true,
33016         /**
33017              * @event complete
33018              * Fires after editing is complete and any changed value has been written to the underlying field.
33019              * @param {Editor} this
33020              * @param {Mixed} value The current field value
33021              * @param {Mixed} startValue The original field value
33022              */
33023         "complete" : true,
33024         /**
33025          * @event specialkey
33026          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33027          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33028          * @param {Roo.form.Field} this
33029          * @param {Roo.EventObject} e The event object
33030          */
33031         "specialkey" : true
33032     });
33033 };
33034
33035 Roo.extend(Roo.Editor, Roo.Component, {
33036     /**
33037      * @cfg {Boolean/String} autosize
33038      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33039      * or "height" to adopt the height only (defaults to false)
33040      */
33041     /**
33042      * @cfg {Boolean} revertInvalid
33043      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33044      * validation fails (defaults to true)
33045      */
33046     /**
33047      * @cfg {Boolean} ignoreNoChange
33048      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33049      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33050      * will never be ignored.
33051      */
33052     /**
33053      * @cfg {Boolean} hideEl
33054      * False to keep the bound element visible while the editor is displayed (defaults to true)
33055      */
33056     /**
33057      * @cfg {Mixed} value
33058      * The data value of the underlying field (defaults to "")
33059      */
33060     value : "",
33061     /**
33062      * @cfg {String} alignment
33063      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33064      */
33065     alignment: "c-c?",
33066     /**
33067      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33068      * for bottom-right shadow (defaults to "frame")
33069      */
33070     shadow : "frame",
33071     /**
33072      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33073      */
33074     constrain : false,
33075     /**
33076      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33077      */
33078     completeOnEnter : false,
33079     /**
33080      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33081      */
33082     cancelOnEsc : false,
33083     /**
33084      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33085      */
33086     updateEl : false,
33087
33088     // private
33089     onRender : function(ct, position){
33090         this.el = new Roo.Layer({
33091             shadow: this.shadow,
33092             cls: "x-editor",
33093             parentEl : ct,
33094             shim : this.shim,
33095             shadowOffset:4,
33096             id: this.id,
33097             constrain: this.constrain
33098         });
33099         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33100         if(this.field.msgTarget != 'title'){
33101             this.field.msgTarget = 'qtip';
33102         }
33103         this.field.render(this.el);
33104         if(Roo.isGecko){
33105             this.field.el.dom.setAttribute('autocomplete', 'off');
33106         }
33107         this.field.on("specialkey", this.onSpecialKey, this);
33108         if(this.swallowKeys){
33109             this.field.el.swallowEvent(['keydown','keypress']);
33110         }
33111         this.field.show();
33112         this.field.on("blur", this.onBlur, this);
33113         if(this.field.grow){
33114             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33115         }
33116     },
33117
33118     onSpecialKey : function(field, e)
33119     {
33120         //Roo.log('editor onSpecialKey');
33121         if(this.completeOnEnter && e.getKey() == e.ENTER){
33122             e.stopEvent();
33123             this.completeEdit();
33124             return;
33125         }
33126         // do not fire special key otherwise it might hide close the editor...
33127         if(e.getKey() == e.ENTER){    
33128             return;
33129         }
33130         if(this.cancelOnEsc && e.getKey() == e.ESC){
33131             this.cancelEdit();
33132             return;
33133         } 
33134         this.fireEvent('specialkey', field, e);
33135     
33136     },
33137
33138     /**
33139      * Starts the editing process and shows the editor.
33140      * @param {String/HTMLElement/Element} el The element to edit
33141      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33142       * to the innerHTML of el.
33143      */
33144     startEdit : function(el, value){
33145         if(this.editing){
33146             this.completeEdit();
33147         }
33148         this.boundEl = Roo.get(el);
33149         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33150         if(!this.rendered){
33151             this.render(this.parentEl || document.body);
33152         }
33153         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33154             return;
33155         }
33156         this.startValue = v;
33157         this.field.setValue(v);
33158         if(this.autoSize){
33159             var sz = this.boundEl.getSize();
33160             switch(this.autoSize){
33161                 case "width":
33162                 this.setSize(sz.width,  "");
33163                 break;
33164                 case "height":
33165                 this.setSize("",  sz.height);
33166                 break;
33167                 default:
33168                 this.setSize(sz.width,  sz.height);
33169             }
33170         }
33171         this.el.alignTo(this.boundEl, this.alignment);
33172         this.editing = true;
33173         if(Roo.QuickTips){
33174             Roo.QuickTips.disable();
33175         }
33176         this.show();
33177     },
33178
33179     /**
33180      * Sets the height and width of this editor.
33181      * @param {Number} width The new width
33182      * @param {Number} height The new height
33183      */
33184     setSize : function(w, h){
33185         this.field.setSize(w, h);
33186         if(this.el){
33187             this.el.sync();
33188         }
33189     },
33190
33191     /**
33192      * Realigns the editor to the bound field based on the current alignment config value.
33193      */
33194     realign : function(){
33195         this.el.alignTo(this.boundEl, this.alignment);
33196     },
33197
33198     /**
33199      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33200      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33201      */
33202     completeEdit : function(remainVisible){
33203         if(!this.editing){
33204             return;
33205         }
33206         var v = this.getValue();
33207         if(this.revertInvalid !== false && !this.field.isValid()){
33208             v = this.startValue;
33209             this.cancelEdit(true);
33210         }
33211         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33212             this.editing = false;
33213             this.hide();
33214             return;
33215         }
33216         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33217             this.editing = false;
33218             if(this.updateEl && this.boundEl){
33219                 this.boundEl.update(v);
33220             }
33221             if(remainVisible !== true){
33222                 this.hide();
33223             }
33224             this.fireEvent("complete", this, v, this.startValue);
33225         }
33226     },
33227
33228     // private
33229     onShow : function(){
33230         this.el.show();
33231         if(this.hideEl !== false){
33232             this.boundEl.hide();
33233         }
33234         this.field.show();
33235         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33236             this.fixIEFocus = true;
33237             this.deferredFocus.defer(50, this);
33238         }else{
33239             this.field.focus();
33240         }
33241         this.fireEvent("startedit", this.boundEl, this.startValue);
33242     },
33243
33244     deferredFocus : function(){
33245         if(this.editing){
33246             this.field.focus();
33247         }
33248     },
33249
33250     /**
33251      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33252      * reverted to the original starting value.
33253      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33254      * cancel (defaults to false)
33255      */
33256     cancelEdit : function(remainVisible){
33257         if(this.editing){
33258             this.setValue(this.startValue);
33259             if(remainVisible !== true){
33260                 this.hide();
33261             }
33262         }
33263     },
33264
33265     // private
33266     onBlur : function(){
33267         if(this.allowBlur !== true && this.editing){
33268             this.completeEdit();
33269         }
33270     },
33271
33272     // private
33273     onHide : function(){
33274         if(this.editing){
33275             this.completeEdit();
33276             return;
33277         }
33278         this.field.blur();
33279         if(this.field.collapse){
33280             this.field.collapse();
33281         }
33282         this.el.hide();
33283         if(this.hideEl !== false){
33284             this.boundEl.show();
33285         }
33286         if(Roo.QuickTips){
33287             Roo.QuickTips.enable();
33288         }
33289     },
33290
33291     /**
33292      * Sets the data value of the editor
33293      * @param {Mixed} value Any valid value supported by the underlying field
33294      */
33295     setValue : function(v){
33296         this.field.setValue(v);
33297     },
33298
33299     /**
33300      * Gets the data value of the editor
33301      * @return {Mixed} The data value
33302      */
33303     getValue : function(){
33304         return this.field.getValue();
33305     }
33306 });/*
33307  * Based on:
33308  * Ext JS Library 1.1.1
33309  * Copyright(c) 2006-2007, Ext JS, LLC.
33310  *
33311  * Originally Released Under LGPL - original licence link has changed is not relivant.
33312  *
33313  * Fork - LGPL
33314  * <script type="text/javascript">
33315  */
33316  
33317 /**
33318  * @class Roo.BasicDialog
33319  * @extends Roo.util.Observable
33320  * @parent none builder
33321  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33322  * <pre><code>
33323 var dlg = new Roo.BasicDialog("my-dlg", {
33324     height: 200,
33325     width: 300,
33326     minHeight: 100,
33327     minWidth: 150,
33328     modal: true,
33329     proxyDrag: true,
33330     shadow: true
33331 });
33332 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33333 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33334 dlg.addButton('Cancel', dlg.hide, dlg);
33335 dlg.show();
33336 </code></pre>
33337   <b>A Dialog should always be a direct child of the body element.</b>
33338  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33339  * @cfg {String} title Default text to display in the title bar (defaults to null)
33340  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33341  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33342  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33343  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33344  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33345  * (defaults to null with no animation)
33346  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33347  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33348  * property for valid values (defaults to 'all')
33349  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33350  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33351  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33352  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33353  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33354  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33355  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33356  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33357  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33358  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33359  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33360  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33361  * draggable = true (defaults to false)
33362  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33363  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33364  * shadow (defaults to false)
33365  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33366  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33367  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33368  * @cfg {Array} buttons Array of buttons
33369  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33370  * @constructor
33371  * Create a new BasicDialog.
33372  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33373  * @param {Object} config Configuration options
33374  */
33375 Roo.BasicDialog = function(el, config){
33376     this.el = Roo.get(el);
33377     var dh = Roo.DomHelper;
33378     if(!this.el && config && config.autoCreate){
33379         if(typeof config.autoCreate == "object"){
33380             if(!config.autoCreate.id){
33381                 config.autoCreate.id = el;
33382             }
33383             this.el = dh.append(document.body,
33384                         config.autoCreate, true);
33385         }else{
33386             this.el = dh.append(document.body,
33387                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33388         }
33389     }
33390     el = this.el;
33391     el.setDisplayed(true);
33392     el.hide = this.hideAction;
33393     this.id = el.id;
33394     el.addClass("x-dlg");
33395
33396     Roo.apply(this, config);
33397
33398     this.proxy = el.createProxy("x-dlg-proxy");
33399     this.proxy.hide = this.hideAction;
33400     this.proxy.setOpacity(.5);
33401     this.proxy.hide();
33402
33403     if(config.width){
33404         el.setWidth(config.width);
33405     }
33406     if(config.height){
33407         el.setHeight(config.height);
33408     }
33409     this.size = el.getSize();
33410     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33411         this.xy = [config.x,config.y];
33412     }else{
33413         this.xy = el.getCenterXY(true);
33414     }
33415     /** The header element @type Roo.Element */
33416     this.header = el.child("> .x-dlg-hd");
33417     /** The body element @type Roo.Element */
33418     this.body = el.child("> .x-dlg-bd");
33419     /** The footer element @type Roo.Element */
33420     this.footer = el.child("> .x-dlg-ft");
33421
33422     if(!this.header){
33423         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33424     }
33425     if(!this.body){
33426         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33427     }
33428
33429     this.header.unselectable();
33430     if(this.title){
33431         this.header.update(this.title);
33432     }
33433     // this element allows the dialog to be focused for keyboard event
33434     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33435     this.focusEl.swallowEvent("click", true);
33436
33437     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33438
33439     // wrap the body and footer for special rendering
33440     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33441     if(this.footer){
33442         this.bwrap.dom.appendChild(this.footer.dom);
33443     }
33444
33445     this.bg = this.el.createChild({
33446         tag: "div", cls:"x-dlg-bg",
33447         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33448     });
33449     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33450
33451
33452     if(this.autoScroll !== false && !this.autoTabs){
33453         this.body.setStyle("overflow", "auto");
33454     }
33455
33456     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33457
33458     if(this.closable !== false){
33459         this.el.addClass("x-dlg-closable");
33460         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33461         this.close.on("click", this.closeClick, this);
33462         this.close.addClassOnOver("x-dlg-close-over");
33463     }
33464     if(this.collapsible !== false){
33465         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33466         this.collapseBtn.on("click", this.collapseClick, this);
33467         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33468         this.header.on("dblclick", this.collapseClick, this);
33469     }
33470     if(this.resizable !== false){
33471         this.el.addClass("x-dlg-resizable");
33472         this.resizer = new Roo.Resizable(el, {
33473             minWidth: this.minWidth || 80,
33474             minHeight:this.minHeight || 80,
33475             handles: this.resizeHandles || "all",
33476             pinned: true
33477         });
33478         this.resizer.on("beforeresize", this.beforeResize, this);
33479         this.resizer.on("resize", this.onResize, this);
33480     }
33481     if(this.draggable !== false){
33482         el.addClass("x-dlg-draggable");
33483         if (!this.proxyDrag) {
33484             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33485         }
33486         else {
33487             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33488         }
33489         dd.setHandleElId(this.header.id);
33490         dd.endDrag = this.endMove.createDelegate(this);
33491         dd.startDrag = this.startMove.createDelegate(this);
33492         dd.onDrag = this.onDrag.createDelegate(this);
33493         dd.scroll = false;
33494         this.dd = dd;
33495     }
33496     if(this.modal){
33497         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33498         this.mask.enableDisplayMode("block");
33499         this.mask.hide();
33500         this.el.addClass("x-dlg-modal");
33501     }
33502     if(this.shadow){
33503         this.shadow = new Roo.Shadow({
33504             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33505             offset : this.shadowOffset
33506         });
33507     }else{
33508         this.shadowOffset = 0;
33509     }
33510     if(Roo.useShims && this.shim !== false){
33511         this.shim = this.el.createShim();
33512         this.shim.hide = this.hideAction;
33513         this.shim.hide();
33514     }else{
33515         this.shim = false;
33516     }
33517     if(this.autoTabs){
33518         this.initTabs();
33519     }
33520     if (this.buttons) { 
33521         var bts= this.buttons;
33522         this.buttons = [];
33523         Roo.each(bts, function(b) {
33524             this.addButton(b);
33525         }, this);
33526     }
33527     
33528     
33529     this.addEvents({
33530         /**
33531          * @event keydown
33532          * Fires when a key is pressed
33533          * @param {Roo.BasicDialog} this
33534          * @param {Roo.EventObject} e
33535          */
33536         "keydown" : true,
33537         /**
33538          * @event move
33539          * Fires when this dialog is moved by the user.
33540          * @param {Roo.BasicDialog} this
33541          * @param {Number} x The new page X
33542          * @param {Number} y The new page Y
33543          */
33544         "move" : true,
33545         /**
33546          * @event resize
33547          * Fires when this dialog is resized by the user.
33548          * @param {Roo.BasicDialog} this
33549          * @param {Number} width The new width
33550          * @param {Number} height The new height
33551          */
33552         "resize" : true,
33553         /**
33554          * @event beforehide
33555          * Fires before this dialog is hidden.
33556          * @param {Roo.BasicDialog} this
33557          */
33558         "beforehide" : true,
33559         /**
33560          * @event hide
33561          * Fires when this dialog is hidden.
33562          * @param {Roo.BasicDialog} this
33563          */
33564         "hide" : true,
33565         /**
33566          * @event beforeshow
33567          * Fires before this dialog is shown.
33568          * @param {Roo.BasicDialog} this
33569          */
33570         "beforeshow" : true,
33571         /**
33572          * @event show
33573          * Fires when this dialog is shown.
33574          * @param {Roo.BasicDialog} this
33575          */
33576         "show" : true
33577     });
33578     el.on("keydown", this.onKeyDown, this);
33579     el.on("mousedown", this.toFront, this);
33580     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33581     this.el.hide();
33582     Roo.DialogManager.register(this);
33583     Roo.BasicDialog.superclass.constructor.call(this);
33584 };
33585
33586 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33587     shadowOffset: Roo.isIE ? 6 : 5,
33588     minHeight: 80,
33589     minWidth: 200,
33590     minButtonWidth: 75,
33591     defaultButton: null,
33592     buttonAlign: "right",
33593     tabTag: 'div',
33594     firstShow: true,
33595
33596     /**
33597      * Sets the dialog title text
33598      * @param {String} text The title text to display
33599      * @return {Roo.BasicDialog} this
33600      */
33601     setTitle : function(text){
33602         this.header.update(text);
33603         return this;
33604     },
33605
33606     // private
33607     closeClick : function(){
33608         this.hide();
33609     },
33610
33611     // private
33612     collapseClick : function(){
33613         this[this.collapsed ? "expand" : "collapse"]();
33614     },
33615
33616     /**
33617      * Collapses the dialog to its minimized state (only the title bar is visible).
33618      * Equivalent to the user clicking the collapse dialog button.
33619      */
33620     collapse : function(){
33621         if(!this.collapsed){
33622             this.collapsed = true;
33623             this.el.addClass("x-dlg-collapsed");
33624             this.restoreHeight = this.el.getHeight();
33625             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33626         }
33627     },
33628
33629     /**
33630      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33631      * clicking the expand dialog button.
33632      */
33633     expand : function(){
33634         if(this.collapsed){
33635             this.collapsed = false;
33636             this.el.removeClass("x-dlg-collapsed");
33637             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33638         }
33639     },
33640
33641     /**
33642      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33643      * @return {Roo.TabPanel} The tabs component
33644      */
33645     initTabs : function(){
33646         var tabs = this.getTabs();
33647         while(tabs.getTab(0)){
33648             tabs.removeTab(0);
33649         }
33650         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33651             var dom = el.dom;
33652             tabs.addTab(Roo.id(dom), dom.title);
33653             dom.title = "";
33654         });
33655         tabs.activate(0);
33656         return tabs;
33657     },
33658
33659     // private
33660     beforeResize : function(){
33661         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33662     },
33663
33664     // private
33665     onResize : function(){
33666         this.refreshSize();
33667         this.syncBodyHeight();
33668         this.adjustAssets();
33669         this.focus();
33670         this.fireEvent("resize", this, this.size.width, this.size.height);
33671     },
33672
33673     // private
33674     onKeyDown : function(e){
33675         if(this.isVisible()){
33676             this.fireEvent("keydown", this, e);
33677         }
33678     },
33679
33680     /**
33681      * Resizes the dialog.
33682      * @param {Number} width
33683      * @param {Number} height
33684      * @return {Roo.BasicDialog} this
33685      */
33686     resizeTo : function(width, height){
33687         this.el.setSize(width, height);
33688         this.size = {width: width, height: height};
33689         this.syncBodyHeight();
33690         if(this.fixedcenter){
33691             this.center();
33692         }
33693         if(this.isVisible()){
33694             this.constrainXY();
33695             this.adjustAssets();
33696         }
33697         this.fireEvent("resize", this, width, height);
33698         return this;
33699     },
33700
33701
33702     /**
33703      * Resizes the dialog to fit the specified content size.
33704      * @param {Number} width
33705      * @param {Number} height
33706      * @return {Roo.BasicDialog} this
33707      */
33708     setContentSize : function(w, h){
33709         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33710         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33711         //if(!this.el.isBorderBox()){
33712             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33713             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33714         //}
33715         if(this.tabs){
33716             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33717             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33718         }
33719         this.resizeTo(w, h);
33720         return this;
33721     },
33722
33723     /**
33724      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33725      * executed in response to a particular key being pressed while the dialog is active.
33726      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33727      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33728      * @param {Function} fn The function to call
33729      * @param {Object} scope (optional) The scope of the function
33730      * @return {Roo.BasicDialog} this
33731      */
33732     addKeyListener : function(key, fn, scope){
33733         var keyCode, shift, ctrl, alt;
33734         if(typeof key == "object" && !(key instanceof Array)){
33735             keyCode = key["key"];
33736             shift = key["shift"];
33737             ctrl = key["ctrl"];
33738             alt = key["alt"];
33739         }else{
33740             keyCode = key;
33741         }
33742         var handler = function(dlg, e){
33743             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33744                 var k = e.getKey();
33745                 if(keyCode instanceof Array){
33746                     for(var i = 0, len = keyCode.length; i < len; i++){
33747                         if(keyCode[i] == k){
33748                           fn.call(scope || window, dlg, k, e);
33749                           return;
33750                         }
33751                     }
33752                 }else{
33753                     if(k == keyCode){
33754                         fn.call(scope || window, dlg, k, e);
33755                     }
33756                 }
33757             }
33758         };
33759         this.on("keydown", handler);
33760         return this;
33761     },
33762
33763     /**
33764      * Returns the TabPanel component (creates it if it doesn't exist).
33765      * Note: If you wish to simply check for the existence of tabs without creating them,
33766      * check for a null 'tabs' property.
33767      * @return {Roo.TabPanel} The tabs component
33768      */
33769     getTabs : function(){
33770         if(!this.tabs){
33771             this.el.addClass("x-dlg-auto-tabs");
33772             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33773             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33774         }
33775         return this.tabs;
33776     },
33777
33778     /**
33779      * Adds a button to the footer section of the dialog.
33780      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33781      * object or a valid Roo.DomHelper element config
33782      * @param {Function} handler The function called when the button is clicked
33783      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33784      * @return {Roo.Button} The new button
33785      */
33786     addButton : function(config, handler, scope){
33787         var dh = Roo.DomHelper;
33788         if(!this.footer){
33789             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33790         }
33791         if(!this.btnContainer){
33792             var tb = this.footer.createChild({
33793
33794                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33795                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33796             }, null, true);
33797             this.btnContainer = tb.firstChild.firstChild.firstChild;
33798         }
33799         var bconfig = {
33800             handler: handler,
33801             scope: scope,
33802             minWidth: this.minButtonWidth,
33803             hideParent:true
33804         };
33805         if(typeof config == "string"){
33806             bconfig.text = config;
33807         }else{
33808             if(config.tag){
33809                 bconfig.dhconfig = config;
33810             }else{
33811                 Roo.apply(bconfig, config);
33812             }
33813         }
33814         var fc = false;
33815         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33816             bconfig.position = Math.max(0, bconfig.position);
33817             fc = this.btnContainer.childNodes[bconfig.position];
33818         }
33819          
33820         var btn = new Roo.Button(
33821             fc ? 
33822                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33823                 : this.btnContainer.appendChild(document.createElement("td")),
33824             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33825             bconfig
33826         );
33827         this.syncBodyHeight();
33828         if(!this.buttons){
33829             /**
33830              * Array of all the buttons that have been added to this dialog via addButton
33831              * @type Array
33832              */
33833             this.buttons = [];
33834         }
33835         this.buttons.push(btn);
33836         return btn;
33837     },
33838
33839     /**
33840      * Sets the default button to be focused when the dialog is displayed.
33841      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33842      * @return {Roo.BasicDialog} this
33843      */
33844     setDefaultButton : function(btn){
33845         this.defaultButton = btn;
33846         return this;
33847     },
33848
33849     // private
33850     getHeaderFooterHeight : function(safe){
33851         var height = 0;
33852         if(this.header){
33853            height += this.header.getHeight();
33854         }
33855         if(this.footer){
33856            var fm = this.footer.getMargins();
33857             height += (this.footer.getHeight()+fm.top+fm.bottom);
33858         }
33859         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33860         height += this.centerBg.getPadding("tb");
33861         return height;
33862     },
33863
33864     // private
33865     syncBodyHeight : function()
33866     {
33867         var bd = this.body, // the text
33868             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33869             bw = this.bwrap;
33870         var height = this.size.height - this.getHeaderFooterHeight(false);
33871         bd.setHeight(height-bd.getMargins("tb"));
33872         var hh = this.header.getHeight();
33873         var h = this.size.height-hh;
33874         cb.setHeight(h);
33875         
33876         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33877         bw.setHeight(h-cb.getPadding("tb"));
33878         
33879         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33880         bd.setWidth(bw.getWidth(true));
33881         if(this.tabs){
33882             this.tabs.syncHeight();
33883             if(Roo.isIE){
33884                 this.tabs.el.repaint();
33885             }
33886         }
33887     },
33888
33889     /**
33890      * Restores the previous state of the dialog if Roo.state is configured.
33891      * @return {Roo.BasicDialog} this
33892      */
33893     restoreState : function(){
33894         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33895         if(box && box.width){
33896             this.xy = [box.x, box.y];
33897             this.resizeTo(box.width, box.height);
33898         }
33899         return this;
33900     },
33901
33902     // private
33903     beforeShow : function(){
33904         this.expand();
33905         if(this.fixedcenter){
33906             this.xy = this.el.getCenterXY(true);
33907         }
33908         if(this.modal){
33909             Roo.get(document.body).addClass("x-body-masked");
33910             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33911             this.mask.show();
33912         }
33913         this.constrainXY();
33914     },
33915
33916     // private
33917     animShow : function(){
33918         var b = Roo.get(this.animateTarget).getBox();
33919         this.proxy.setSize(b.width, b.height);
33920         this.proxy.setLocation(b.x, b.y);
33921         this.proxy.show();
33922         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33923                     true, .35, this.showEl.createDelegate(this));
33924     },
33925
33926     /**
33927      * Shows the dialog.
33928      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33929      * @return {Roo.BasicDialog} this
33930      */
33931     show : function(animateTarget){
33932         if (this.fireEvent("beforeshow", this) === false){
33933             return;
33934         }
33935         if(this.syncHeightBeforeShow){
33936             this.syncBodyHeight();
33937         }else if(this.firstShow){
33938             this.firstShow = false;
33939             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33940         }
33941         this.animateTarget = animateTarget || this.animateTarget;
33942         if(!this.el.isVisible()){
33943             this.beforeShow();
33944             if(this.animateTarget && Roo.get(this.animateTarget)){
33945                 this.animShow();
33946             }else{
33947                 this.showEl();
33948             }
33949         }
33950         return this;
33951     },
33952
33953     // private
33954     showEl : function(){
33955         this.proxy.hide();
33956         this.el.setXY(this.xy);
33957         this.el.show();
33958         this.adjustAssets(true);
33959         this.toFront();
33960         this.focus();
33961         // IE peekaboo bug - fix found by Dave Fenwick
33962         if(Roo.isIE){
33963             this.el.repaint();
33964         }
33965         this.fireEvent("show", this);
33966     },
33967
33968     /**
33969      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33970      * dialog itself will receive focus.
33971      */
33972     focus : function(){
33973         if(this.defaultButton){
33974             this.defaultButton.focus();
33975         }else{
33976             this.focusEl.focus();
33977         }
33978     },
33979
33980     // private
33981     constrainXY : function(){
33982         if(this.constraintoviewport !== false){
33983             if(!this.viewSize){
33984                 if(this.container){
33985                     var s = this.container.getSize();
33986                     this.viewSize = [s.width, s.height];
33987                 }else{
33988                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33989                 }
33990             }
33991             var s = Roo.get(this.container||document).getScroll();
33992
33993             var x = this.xy[0], y = this.xy[1];
33994             var w = this.size.width, h = this.size.height;
33995             var vw = this.viewSize[0], vh = this.viewSize[1];
33996             // only move it if it needs it
33997             var moved = false;
33998             // first validate right/bottom
33999             if(x + w > vw+s.left){
34000                 x = vw - w;
34001                 moved = true;
34002             }
34003             if(y + h > vh+s.top){
34004                 y = vh - h;
34005                 moved = true;
34006             }
34007             // then make sure top/left isn't negative
34008             if(x < s.left){
34009                 x = s.left;
34010                 moved = true;
34011             }
34012             if(y < s.top){
34013                 y = s.top;
34014                 moved = true;
34015             }
34016             if(moved){
34017                 // cache xy
34018                 this.xy = [x, y];
34019                 if(this.isVisible()){
34020                     this.el.setLocation(x, y);
34021                     this.adjustAssets();
34022                 }
34023             }
34024         }
34025     },
34026
34027     // private
34028     onDrag : function(){
34029         if(!this.proxyDrag){
34030             this.xy = this.el.getXY();
34031             this.adjustAssets();
34032         }
34033     },
34034
34035     // private
34036     adjustAssets : function(doShow){
34037         var x = this.xy[0], y = this.xy[1];
34038         var w = this.size.width, h = this.size.height;
34039         if(doShow === true){
34040             if(this.shadow){
34041                 this.shadow.show(this.el);
34042             }
34043             if(this.shim){
34044                 this.shim.show();
34045             }
34046         }
34047         if(this.shadow && this.shadow.isVisible()){
34048             this.shadow.show(this.el);
34049         }
34050         if(this.shim && this.shim.isVisible()){
34051             this.shim.setBounds(x, y, w, h);
34052         }
34053     },
34054
34055     // private
34056     adjustViewport : function(w, h){
34057         if(!w || !h){
34058             w = Roo.lib.Dom.getViewWidth();
34059             h = Roo.lib.Dom.getViewHeight();
34060         }
34061         // cache the size
34062         this.viewSize = [w, h];
34063         if(this.modal && this.mask.isVisible()){
34064             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34065             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34066         }
34067         if(this.isVisible()){
34068             this.constrainXY();
34069         }
34070     },
34071
34072     /**
34073      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34074      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34075      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34076      */
34077     destroy : function(removeEl){
34078         if(this.isVisible()){
34079             this.animateTarget = null;
34080             this.hide();
34081         }
34082         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34083         if(this.tabs){
34084             this.tabs.destroy(removeEl);
34085         }
34086         Roo.destroy(
34087              this.shim,
34088              this.proxy,
34089              this.resizer,
34090              this.close,
34091              this.mask
34092         );
34093         if(this.dd){
34094             this.dd.unreg();
34095         }
34096         if(this.buttons){
34097            for(var i = 0, len = this.buttons.length; i < len; i++){
34098                this.buttons[i].destroy();
34099            }
34100         }
34101         this.el.removeAllListeners();
34102         if(removeEl === true){
34103             this.el.update("");
34104             this.el.remove();
34105         }
34106         Roo.DialogManager.unregister(this);
34107     },
34108
34109     // private
34110     startMove : function(){
34111         if(this.proxyDrag){
34112             this.proxy.show();
34113         }
34114         if(this.constraintoviewport !== false){
34115             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34116         }
34117     },
34118
34119     // private
34120     endMove : function(){
34121         if(!this.proxyDrag){
34122             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34123         }else{
34124             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34125             this.proxy.hide();
34126         }
34127         this.refreshSize();
34128         this.adjustAssets();
34129         this.focus();
34130         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34131     },
34132
34133     /**
34134      * Brings this dialog to the front of any other visible dialogs
34135      * @return {Roo.BasicDialog} this
34136      */
34137     toFront : function(){
34138         Roo.DialogManager.bringToFront(this);
34139         return this;
34140     },
34141
34142     /**
34143      * Sends this dialog to the back (under) of any other visible dialogs
34144      * @return {Roo.BasicDialog} this
34145      */
34146     toBack : function(){
34147         Roo.DialogManager.sendToBack(this);
34148         return this;
34149     },
34150
34151     /**
34152      * Centers this dialog in the viewport
34153      * @return {Roo.BasicDialog} this
34154      */
34155     center : function(){
34156         var xy = this.el.getCenterXY(true);
34157         this.moveTo(xy[0], xy[1]);
34158         return this;
34159     },
34160
34161     /**
34162      * Moves the dialog's top-left corner to the specified point
34163      * @param {Number} x
34164      * @param {Number} y
34165      * @return {Roo.BasicDialog} this
34166      */
34167     moveTo : function(x, y){
34168         this.xy = [x,y];
34169         if(this.isVisible()){
34170             this.el.setXY(this.xy);
34171             this.adjustAssets();
34172         }
34173         return this;
34174     },
34175
34176     /**
34177      * Aligns the dialog to the specified element
34178      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34179      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34180      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34181      * @return {Roo.BasicDialog} this
34182      */
34183     alignTo : function(element, position, offsets){
34184         this.xy = this.el.getAlignToXY(element, position, offsets);
34185         if(this.isVisible()){
34186             this.el.setXY(this.xy);
34187             this.adjustAssets();
34188         }
34189         return this;
34190     },
34191
34192     /**
34193      * Anchors an element to another element and realigns it when the window is resized.
34194      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34195      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34196      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34197      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34198      * is a number, it is used as the buffer delay (defaults to 50ms).
34199      * @return {Roo.BasicDialog} this
34200      */
34201     anchorTo : function(el, alignment, offsets, monitorScroll){
34202         var action = function(){
34203             this.alignTo(el, alignment, offsets);
34204         };
34205         Roo.EventManager.onWindowResize(action, this);
34206         var tm = typeof monitorScroll;
34207         if(tm != 'undefined'){
34208             Roo.EventManager.on(window, 'scroll', action, this,
34209                 {buffer: tm == 'number' ? monitorScroll : 50});
34210         }
34211         action.call(this);
34212         return this;
34213     },
34214
34215     /**
34216      * Returns true if the dialog is visible
34217      * @return {Boolean}
34218      */
34219     isVisible : function(){
34220         return this.el.isVisible();
34221     },
34222
34223     // private
34224     animHide : function(callback){
34225         var b = Roo.get(this.animateTarget).getBox();
34226         this.proxy.show();
34227         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34228         this.el.hide();
34229         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34230                     this.hideEl.createDelegate(this, [callback]));
34231     },
34232
34233     /**
34234      * Hides the dialog.
34235      * @param {Function} callback (optional) Function to call when the dialog is hidden
34236      * @return {Roo.BasicDialog} this
34237      */
34238     hide : function(callback){
34239         if (this.fireEvent("beforehide", this) === false){
34240             return;
34241         }
34242         if(this.shadow){
34243             this.shadow.hide();
34244         }
34245         if(this.shim) {
34246           this.shim.hide();
34247         }
34248         // sometimes animateTarget seems to get set.. causing problems...
34249         // this just double checks..
34250         if(this.animateTarget && Roo.get(this.animateTarget)) {
34251            this.animHide(callback);
34252         }else{
34253             this.el.hide();
34254             this.hideEl(callback);
34255         }
34256         return this;
34257     },
34258
34259     // private
34260     hideEl : function(callback){
34261         this.proxy.hide();
34262         if(this.modal){
34263             this.mask.hide();
34264             Roo.get(document.body).removeClass("x-body-masked");
34265         }
34266         this.fireEvent("hide", this);
34267         if(typeof callback == "function"){
34268             callback();
34269         }
34270     },
34271
34272     // private
34273     hideAction : function(){
34274         this.setLeft("-10000px");
34275         this.setTop("-10000px");
34276         this.setStyle("visibility", "hidden");
34277     },
34278
34279     // private
34280     refreshSize : function(){
34281         this.size = this.el.getSize();
34282         this.xy = this.el.getXY();
34283         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34284     },
34285
34286     // private
34287     // z-index is managed by the DialogManager and may be overwritten at any time
34288     setZIndex : function(index){
34289         if(this.modal){
34290             this.mask.setStyle("z-index", index);
34291         }
34292         if(this.shim){
34293             this.shim.setStyle("z-index", ++index);
34294         }
34295         if(this.shadow){
34296             this.shadow.setZIndex(++index);
34297         }
34298         this.el.setStyle("z-index", ++index);
34299         if(this.proxy){
34300             this.proxy.setStyle("z-index", ++index);
34301         }
34302         if(this.resizer){
34303             this.resizer.proxy.setStyle("z-index", ++index);
34304         }
34305
34306         this.lastZIndex = index;
34307     },
34308
34309     /**
34310      * Returns the element for this dialog
34311      * @return {Roo.Element} The underlying dialog Element
34312      */
34313     getEl : function(){
34314         return this.el;
34315     }
34316 });
34317
34318 /**
34319  * @class Roo.DialogManager
34320  * Provides global access to BasicDialogs that have been created and
34321  * support for z-indexing (layering) multiple open dialogs.
34322  */
34323 Roo.DialogManager = function(){
34324     var list = {};
34325     var accessList = [];
34326     var front = null;
34327
34328     // private
34329     var sortDialogs = function(d1, d2){
34330         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34331     };
34332
34333     // private
34334     var orderDialogs = function(){
34335         accessList.sort(sortDialogs);
34336         var seed = Roo.DialogManager.zseed;
34337         for(var i = 0, len = accessList.length; i < len; i++){
34338             var dlg = accessList[i];
34339             if(dlg){
34340                 dlg.setZIndex(seed + (i*10));
34341             }
34342         }
34343     };
34344
34345     return {
34346         /**
34347          * The starting z-index for BasicDialogs (defaults to 9000)
34348          * @type Number The z-index value
34349          */
34350         zseed : 9000,
34351
34352         // private
34353         register : function(dlg){
34354             list[dlg.id] = dlg;
34355             accessList.push(dlg);
34356         },
34357
34358         // private
34359         unregister : function(dlg){
34360             delete list[dlg.id];
34361             var i=0;
34362             var len=0;
34363             if(!accessList.indexOf){
34364                 for(  i = 0, len = accessList.length; i < len; i++){
34365                     if(accessList[i] == dlg){
34366                         accessList.splice(i, 1);
34367                         return;
34368                     }
34369                 }
34370             }else{
34371                  i = accessList.indexOf(dlg);
34372                 if(i != -1){
34373                     accessList.splice(i, 1);
34374                 }
34375             }
34376         },
34377
34378         /**
34379          * Gets a registered dialog by id
34380          * @param {String/Object} id The id of the dialog or a dialog
34381          * @return {Roo.BasicDialog} this
34382          */
34383         get : function(id){
34384             return typeof id == "object" ? id : list[id];
34385         },
34386
34387         /**
34388          * Brings the specified dialog to the front
34389          * @param {String/Object} dlg The id of the dialog or a dialog
34390          * @return {Roo.BasicDialog} this
34391          */
34392         bringToFront : function(dlg){
34393             dlg = this.get(dlg);
34394             if(dlg != front){
34395                 front = dlg;
34396                 dlg._lastAccess = new Date().getTime();
34397                 orderDialogs();
34398             }
34399             return dlg;
34400         },
34401
34402         /**
34403          * Sends the specified dialog to the back
34404          * @param {String/Object} dlg The id of the dialog or a dialog
34405          * @return {Roo.BasicDialog} this
34406          */
34407         sendToBack : function(dlg){
34408             dlg = this.get(dlg);
34409             dlg._lastAccess = -(new Date().getTime());
34410             orderDialogs();
34411             return dlg;
34412         },
34413
34414         /**
34415          * Hides all dialogs
34416          */
34417         hideAll : function(){
34418             for(var id in list){
34419                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34420                     list[id].hide();
34421                 }
34422             }
34423         }
34424     };
34425 }();
34426
34427 /**
34428  * @class Roo.LayoutDialog
34429  * @extends Roo.BasicDialog
34430  * @children Roo.ContentPanel
34431  * @parent builder none
34432  * Dialog which provides adjustments for working with a layout in a Dialog.
34433  * Add your necessary layout config options to the dialog's config.<br>
34434  * Example usage (including a nested layout):
34435  * <pre><code>
34436 if(!dialog){
34437     dialog = new Roo.LayoutDialog("download-dlg", {
34438         modal: true,
34439         width:600,
34440         height:450,
34441         shadow:true,
34442         minWidth:500,
34443         minHeight:350,
34444         autoTabs:true,
34445         proxyDrag:true,
34446         // layout config merges with the dialog config
34447         center:{
34448             tabPosition: "top",
34449             alwaysShowTabs: true
34450         }
34451     });
34452     dialog.addKeyListener(27, dialog.hide, dialog);
34453     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34454     dialog.addButton("Build It!", this.getDownload, this);
34455
34456     // we can even add nested layouts
34457     var innerLayout = new Roo.BorderLayout("dl-inner", {
34458         east: {
34459             initialSize: 200,
34460             autoScroll:true,
34461             split:true
34462         },
34463         center: {
34464             autoScroll:true
34465         }
34466     });
34467     innerLayout.beginUpdate();
34468     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34469     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34470     innerLayout.endUpdate(true);
34471
34472     var layout = dialog.getLayout();
34473     layout.beginUpdate();
34474     layout.add("center", new Roo.ContentPanel("standard-panel",
34475                         {title: "Download the Source", fitToFrame:true}));
34476     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34477                {title: "Build your own roo.js"}));
34478     layout.getRegion("center").showPanel(sp);
34479     layout.endUpdate();
34480 }
34481 </code></pre>
34482     * @constructor
34483     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34484     * @param {Object} config configuration options
34485   */
34486 Roo.LayoutDialog = function(el, cfg){
34487     
34488     var config=  cfg;
34489     if (typeof(cfg) == 'undefined') {
34490         config = Roo.apply({}, el);
34491         // not sure why we use documentElement here.. - it should always be body.
34492         // IE7 borks horribly if we use documentElement.
34493         // webkit also does not like documentElement - it creates a body element...
34494         el = Roo.get( document.body || document.documentElement ).createChild();
34495         //config.autoCreate = true;
34496     }
34497     
34498     
34499     config.autoTabs = false;
34500     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34501     this.body.setStyle({overflow:"hidden", position:"relative"});
34502     this.layout = new Roo.BorderLayout(this.body.dom, config);
34503     this.layout.monitorWindowResize = false;
34504     this.el.addClass("x-dlg-auto-layout");
34505     // fix case when center region overwrites center function
34506     this.center = Roo.BasicDialog.prototype.center;
34507     this.on("show", this.layout.layout, this.layout, true);
34508     if (config.items) {
34509         var xitems = config.items;
34510         delete config.items;
34511         Roo.each(xitems, this.addxtype, this);
34512     }
34513     
34514     
34515 };
34516 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34517     
34518     
34519     /**
34520      * @cfg {Roo.LayoutRegion} east  
34521      */
34522     /**
34523      * @cfg {Roo.LayoutRegion} west
34524      */
34525     /**
34526      * @cfg {Roo.LayoutRegion} south
34527      */
34528     /**
34529      * @cfg {Roo.LayoutRegion} north
34530      */
34531     /**
34532      * @cfg {Roo.LayoutRegion} center
34533      */
34534     /**
34535      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34536      */
34537     
34538     
34539     /**
34540      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34541      * @deprecated
34542      */
34543     endUpdate : function(){
34544         this.layout.endUpdate();
34545     },
34546
34547     /**
34548      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34549      *  @deprecated
34550      */
34551     beginUpdate : function(){
34552         this.layout.beginUpdate();
34553     },
34554
34555     /**
34556      * Get the BorderLayout for this dialog
34557      * @return {Roo.BorderLayout}
34558      */
34559     getLayout : function(){
34560         return this.layout;
34561     },
34562
34563     showEl : function(){
34564         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34565         if(Roo.isIE7){
34566             this.layout.layout();
34567         }
34568     },
34569
34570     // private
34571     // Use the syncHeightBeforeShow config option to control this automatically
34572     syncBodyHeight : function(){
34573         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34574         if(this.layout){this.layout.layout();}
34575     },
34576     
34577       /**
34578      * Add an xtype element (actually adds to the layout.)
34579      * @return {Object} xdata xtype object data.
34580      */
34581     
34582     addxtype : function(c) {
34583         return this.layout.addxtype(c);
34584     }
34585 });/*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595  
34596 /**
34597  * @class Roo.MessageBox
34598  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34599  * Example usage:
34600  *<pre><code>
34601 // Basic alert:
34602 Roo.Msg.alert('Status', 'Changes saved successfully.');
34603
34604 // Prompt for user data:
34605 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34606     if (btn == 'ok'){
34607         // process text value...
34608     }
34609 });
34610
34611 // Show a dialog using config options:
34612 Roo.Msg.show({
34613    title:'Save Changes?',
34614    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34615    buttons: Roo.Msg.YESNOCANCEL,
34616    fn: processResult,
34617    animEl: 'elId'
34618 });
34619 </code></pre>
34620  * @static
34621  */
34622 Roo.MessageBox = function(){
34623     var dlg, opt, mask, waitTimer;
34624     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34625     var buttons, activeTextEl, bwidth;
34626
34627     // private
34628     var handleButton = function(button){
34629         dlg.hide();
34630         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34631     };
34632
34633     // private
34634     var handleHide = function(){
34635         if(opt && opt.cls){
34636             dlg.el.removeClass(opt.cls);
34637         }
34638         if(waitTimer){
34639             Roo.TaskMgr.stop(waitTimer);
34640             waitTimer = null;
34641         }
34642     };
34643
34644     // private
34645     var updateButtons = function(b){
34646         var width = 0;
34647         if(!b){
34648             buttons["ok"].hide();
34649             buttons["cancel"].hide();
34650             buttons["yes"].hide();
34651             buttons["no"].hide();
34652             dlg.footer.dom.style.display = 'none';
34653             return width;
34654         }
34655         dlg.footer.dom.style.display = '';
34656         for(var k in buttons){
34657             if(typeof buttons[k] != "function"){
34658                 if(b[k]){
34659                     buttons[k].show();
34660                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34661                     width += buttons[k].el.getWidth()+15;
34662                 }else{
34663                     buttons[k].hide();
34664                 }
34665             }
34666         }
34667         return width;
34668     };
34669
34670     // private
34671     var handleEsc = function(d, k, e){
34672         if(opt && opt.closable !== false){
34673             dlg.hide();
34674         }
34675         if(e){
34676             e.stopEvent();
34677         }
34678     };
34679
34680     return {
34681         /**
34682          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34683          * @return {Roo.BasicDialog} The BasicDialog element
34684          */
34685         getDialog : function(){
34686            if(!dlg){
34687                 dlg = new Roo.BasicDialog("x-msg-box", {
34688                     autoCreate : true,
34689                     shadow: true,
34690                     draggable: true,
34691                     resizable:false,
34692                     constraintoviewport:false,
34693                     fixedcenter:true,
34694                     collapsible : false,
34695                     shim:true,
34696                     modal: true,
34697                     width:400, height:100,
34698                     buttonAlign:"center",
34699                     closeClick : function(){
34700                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34701                             handleButton("no");
34702                         }else{
34703                             handleButton("cancel");
34704                         }
34705                     }
34706                 });
34707                 dlg.on("hide", handleHide);
34708                 mask = dlg.mask;
34709                 dlg.addKeyListener(27, handleEsc);
34710                 buttons = {};
34711                 var bt = this.buttonText;
34712                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34713                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34714                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34715                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34716                 bodyEl = dlg.body.createChild({
34717
34718                     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>'
34719                 });
34720                 msgEl = bodyEl.dom.firstChild;
34721                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34722                 textboxEl.enableDisplayMode();
34723                 textboxEl.addKeyListener([10,13], function(){
34724                     if(dlg.isVisible() && opt && opt.buttons){
34725                         if(opt.buttons.ok){
34726                             handleButton("ok");
34727                         }else if(opt.buttons.yes){
34728                             handleButton("yes");
34729                         }
34730                     }
34731                 });
34732                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34733                 textareaEl.enableDisplayMode();
34734                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34735                 progressEl.enableDisplayMode();
34736                 var pf = progressEl.dom.firstChild;
34737                 if (pf) {
34738                     pp = Roo.get(pf.firstChild);
34739                     pp.setHeight(pf.offsetHeight);
34740                 }
34741                 
34742             }
34743             return dlg;
34744         },
34745
34746         /**
34747          * Updates the message box body text
34748          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34749          * the XHTML-compliant non-breaking space character '&amp;#160;')
34750          * @return {Roo.MessageBox} This message box
34751          */
34752         updateText : function(text){
34753             if(!dlg.isVisible() && !opt.width){
34754                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34755             }
34756             msgEl.innerHTML = text || '&#160;';
34757       
34758             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34759             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34760             var w = Math.max(
34761                     Math.min(opt.width || cw , this.maxWidth), 
34762                     Math.max(opt.minWidth || this.minWidth, bwidth)
34763             );
34764             if(opt.prompt){
34765                 activeTextEl.setWidth(w);
34766             }
34767             if(dlg.isVisible()){
34768                 dlg.fixedcenter = false;
34769             }
34770             // to big, make it scroll. = But as usual stupid IE does not support
34771             // !important..
34772             
34773             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34774                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34775                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34776             } else {
34777                 bodyEl.dom.style.height = '';
34778                 bodyEl.dom.style.overflowY = '';
34779             }
34780             if (cw > w) {
34781                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34782             } else {
34783                 bodyEl.dom.style.overflowX = '';
34784             }
34785             
34786             dlg.setContentSize(w, bodyEl.getHeight());
34787             if(dlg.isVisible()){
34788                 dlg.fixedcenter = true;
34789             }
34790             return this;
34791         },
34792
34793         /**
34794          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34795          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34796          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34797          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34798          * @return {Roo.MessageBox} This message box
34799          */
34800         updateProgress : function(value, text){
34801             if(text){
34802                 this.updateText(text);
34803             }
34804             if (pp) { // weird bug on my firefox - for some reason this is not defined
34805                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34806             }
34807             return this;
34808         },        
34809
34810         /**
34811          * Returns true if the message box is currently displayed
34812          * @return {Boolean} True if the message box is visible, else false
34813          */
34814         isVisible : function(){
34815             return dlg && dlg.isVisible();  
34816         },
34817
34818         /**
34819          * Hides the message box if it is displayed
34820          */
34821         hide : function(){
34822             if(this.isVisible()){
34823                 dlg.hide();
34824             }  
34825         },
34826
34827         /**
34828          * Displays a new message box, or reinitializes an existing message box, based on the config options
34829          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34830          * The following config object properties are supported:
34831          * <pre>
34832 Property    Type             Description
34833 ----------  ---------------  ------------------------------------------------------------------------------------
34834 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34835                                    closes (defaults to undefined)
34836 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34837                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34838 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34839                                    progress and wait dialogs will ignore this property and always hide the
34840                                    close button as they can only be closed programmatically.
34841 cls               String           A custom CSS class to apply to the message box element
34842 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34843                                    displayed (defaults to 75)
34844 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34845                                    function will be btn (the name of the button that was clicked, if applicable,
34846                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34847                                    Progress and wait dialogs will ignore this option since they do not respond to
34848                                    user actions and can only be closed programmatically, so any required function
34849                                    should be called by the same code after it closes the dialog.
34850 icon              String           A CSS class that provides a background image to be used as an icon for
34851                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34852 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34853 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34854 modal             Boolean          False to allow user interaction with the page while the message box is
34855                                    displayed (defaults to true)
34856 msg               String           A string that will replace the existing message box body text (defaults
34857                                    to the XHTML-compliant non-breaking space character '&#160;')
34858 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34859 progress          Boolean          True to display a progress bar (defaults to false)
34860 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34861 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34862 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34863 title             String           The title text
34864 value             String           The string value to set into the active textbox element if displayed
34865 wait              Boolean          True to display a progress bar (defaults to false)
34866 width             Number           The width of the dialog in pixels
34867 </pre>
34868          *
34869          * Example usage:
34870          * <pre><code>
34871 Roo.Msg.show({
34872    title: 'Address',
34873    msg: 'Please enter your address:',
34874    width: 300,
34875    buttons: Roo.MessageBox.OKCANCEL,
34876    multiline: true,
34877    fn: saveAddress,
34878    animEl: 'addAddressBtn'
34879 });
34880 </code></pre>
34881          * @param {Object} config Configuration options
34882          * @return {Roo.MessageBox} This message box
34883          */
34884         show : function(options)
34885         {
34886             
34887             // this causes nightmares if you show one dialog after another
34888             // especially on callbacks..
34889              
34890             if(this.isVisible()){
34891                 
34892                 this.hide();
34893                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34894                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34895                 Roo.log("New Dialog Message:" +  options.msg )
34896                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34897                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34898                 
34899             }
34900             var d = this.getDialog();
34901             opt = options;
34902             d.setTitle(opt.title || "&#160;");
34903             d.close.setDisplayed(opt.closable !== false);
34904             activeTextEl = textboxEl;
34905             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34906             if(opt.prompt){
34907                 if(opt.multiline){
34908                     textboxEl.hide();
34909                     textareaEl.show();
34910                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34911                         opt.multiline : this.defaultTextHeight);
34912                     activeTextEl = textareaEl;
34913                 }else{
34914                     textboxEl.show();
34915                     textareaEl.hide();
34916                 }
34917             }else{
34918                 textboxEl.hide();
34919                 textareaEl.hide();
34920             }
34921             progressEl.setDisplayed(opt.progress === true);
34922             this.updateProgress(0);
34923             activeTextEl.dom.value = opt.value || "";
34924             if(opt.prompt){
34925                 dlg.setDefaultButton(activeTextEl);
34926             }else{
34927                 var bs = opt.buttons;
34928                 var db = null;
34929                 if(bs && bs.ok){
34930                     db = buttons["ok"];
34931                 }else if(bs && bs.yes){
34932                     db = buttons["yes"];
34933                 }
34934                 dlg.setDefaultButton(db);
34935             }
34936             bwidth = updateButtons(opt.buttons);
34937             this.updateText(opt.msg);
34938             if(opt.cls){
34939                 d.el.addClass(opt.cls);
34940             }
34941             d.proxyDrag = opt.proxyDrag === true;
34942             d.modal = opt.modal !== false;
34943             d.mask = opt.modal !== false ? mask : false;
34944             if(!d.isVisible()){
34945                 // force it to the end of the z-index stack so it gets a cursor in FF
34946                 document.body.appendChild(dlg.el.dom);
34947                 d.animateTarget = null;
34948                 d.show(options.animEl);
34949             }
34950             return this;
34951         },
34952
34953         /**
34954          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34955          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34956          * and closing the message box when the process is complete.
34957          * @param {String} title The title bar text
34958          * @param {String} msg The message box body text
34959          * @return {Roo.MessageBox} This message box
34960          */
34961         progress : function(title, msg){
34962             this.show({
34963                 title : title,
34964                 msg : msg,
34965                 buttons: false,
34966                 progress:true,
34967                 closable:false,
34968                 minWidth: this.minProgressWidth,
34969                 modal : true
34970             });
34971             return this;
34972         },
34973
34974         /**
34975          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34976          * If a callback function is passed it will be called after the user clicks the button, and the
34977          * id of the button that was clicked will be passed as the only parameter to the callback
34978          * (could also be the top-right close button).
34979          * @param {String} title The title bar text
34980          * @param {String} msg The message box body text
34981          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34982          * @param {Object} scope (optional) The scope of the callback function
34983          * @return {Roo.MessageBox} This message box
34984          */
34985         alert : function(title, msg, fn, scope){
34986             this.show({
34987                 title : title,
34988                 msg : msg,
34989                 buttons: this.OK,
34990                 fn: fn,
34991                 scope : scope,
34992                 modal : true
34993             });
34994             return this;
34995         },
34996
34997         /**
34998          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34999          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35000          * You are responsible for closing the message box when the process is complete.
35001          * @param {String} msg The message box body text
35002          * @param {String} title (optional) The title bar text
35003          * @return {Roo.MessageBox} This message box
35004          */
35005         wait : function(msg, title){
35006             this.show({
35007                 title : title,
35008                 msg : msg,
35009                 buttons: false,
35010                 closable:false,
35011                 progress:true,
35012                 modal:true,
35013                 width:300,
35014                 wait:true
35015             });
35016             waitTimer = Roo.TaskMgr.start({
35017                 run: function(i){
35018                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35019                 },
35020                 interval: 1000
35021             });
35022             return this;
35023         },
35024
35025         /**
35026          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35027          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35028          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35029          * @param {String} title The title bar text
35030          * @param {String} msg The message box body text
35031          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35032          * @param {Object} scope (optional) The scope of the callback function
35033          * @return {Roo.MessageBox} This message box
35034          */
35035         confirm : function(title, msg, fn, scope){
35036             this.show({
35037                 title : title,
35038                 msg : msg,
35039                 buttons: this.YESNO,
35040                 fn: fn,
35041                 scope : scope,
35042                 modal : true
35043             });
35044             return this;
35045         },
35046
35047         /**
35048          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35049          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35050          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35051          * (could also be the top-right close button) and the text that was entered will be passed as the two
35052          * parameters to the callback.
35053          * @param {String} title The title bar text
35054          * @param {String} msg The message box body text
35055          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35056          * @param {Object} scope (optional) The scope of the callback function
35057          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35058          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35059          * @return {Roo.MessageBox} This message box
35060          */
35061         prompt : function(title, msg, fn, scope, multiline){
35062             this.show({
35063                 title : title,
35064                 msg : msg,
35065                 buttons: this.OKCANCEL,
35066                 fn: fn,
35067                 minWidth:250,
35068                 scope : scope,
35069                 prompt:true,
35070                 multiline: multiline,
35071                 modal : true
35072             });
35073             return this;
35074         },
35075
35076         /**
35077          * Button config that displays a single OK button
35078          * @type Object
35079          */
35080         OK : {ok:true},
35081         /**
35082          * Button config that displays Yes and No buttons
35083          * @type Object
35084          */
35085         YESNO : {yes:true, no:true},
35086         /**
35087          * Button config that displays OK and Cancel buttons
35088          * @type Object
35089          */
35090         OKCANCEL : {ok:true, cancel:true},
35091         /**
35092          * Button config that displays Yes, No and Cancel buttons
35093          * @type Object
35094          */
35095         YESNOCANCEL : {yes:true, no:true, cancel:true},
35096
35097         /**
35098          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35099          * @type Number
35100          */
35101         defaultTextHeight : 75,
35102         /**
35103          * The maximum width in pixels of the message box (defaults to 600)
35104          * @type Number
35105          */
35106         maxWidth : 600,
35107         /**
35108          * The minimum width in pixels of the message box (defaults to 100)
35109          * @type Number
35110          */
35111         minWidth : 100,
35112         /**
35113          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35114          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35115          * @type Number
35116          */
35117         minProgressWidth : 250,
35118         /**
35119          * An object containing the default button text strings that can be overriden for localized language support.
35120          * Supported properties are: ok, cancel, yes and no.
35121          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35122          * @type Object
35123          */
35124         buttonText : {
35125             ok : "OK",
35126             cancel : "Cancel",
35127             yes : "Yes",
35128             no : "No"
35129         }
35130     };
35131 }();
35132
35133 /**
35134  * Shorthand for {@link Roo.MessageBox}
35135  */
35136 Roo.Msg = Roo.MessageBox;/*
35137  * Based on:
35138  * Ext JS Library 1.1.1
35139  * Copyright(c) 2006-2007, Ext JS, LLC.
35140  *
35141  * Originally Released Under LGPL - original licence link has changed is not relivant.
35142  *
35143  * Fork - LGPL
35144  * <script type="text/javascript">
35145  */
35146 /**
35147  * @class Roo.QuickTips
35148  * Provides attractive and customizable tooltips for any element.
35149  * @static
35150  */
35151 Roo.QuickTips = function(){
35152     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35153     var ce, bd, xy, dd;
35154     var visible = false, disabled = true, inited = false;
35155     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35156     
35157     var onOver = function(e){
35158         if(disabled){
35159             return;
35160         }
35161         var t = e.getTarget();
35162         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35163             return;
35164         }
35165         if(ce && t == ce.el){
35166             clearTimeout(hideProc);
35167             return;
35168         }
35169         if(t && tagEls[t.id]){
35170             tagEls[t.id].el = t;
35171             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35172             return;
35173         }
35174         var ttp, et = Roo.fly(t);
35175         var ns = cfg.namespace;
35176         if(tm.interceptTitles && t.title){
35177             ttp = t.title;
35178             t.qtip = ttp;
35179             t.removeAttribute("title");
35180             e.preventDefault();
35181         }else{
35182             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35183         }
35184         if(ttp){
35185             showProc = show.defer(tm.showDelay, tm, [{
35186                 el: t, 
35187                 text: ttp.replace(/\\n/g,'<br/>'),
35188                 width: et.getAttributeNS(ns, cfg.width),
35189                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35190                 title: et.getAttributeNS(ns, cfg.title),
35191                     cls: et.getAttributeNS(ns, cfg.cls)
35192             }]);
35193         }
35194     };
35195     
35196     var onOut = function(e){
35197         clearTimeout(showProc);
35198         var t = e.getTarget();
35199         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35200             hideProc = setTimeout(hide, tm.hideDelay);
35201         }
35202     };
35203     
35204     var onMove = function(e){
35205         if(disabled){
35206             return;
35207         }
35208         xy = e.getXY();
35209         xy[1] += 18;
35210         if(tm.trackMouse && ce){
35211             el.setXY(xy);
35212         }
35213     };
35214     
35215     var onDown = function(e){
35216         clearTimeout(showProc);
35217         clearTimeout(hideProc);
35218         if(!e.within(el)){
35219             if(tm.hideOnClick){
35220                 hide();
35221                 tm.disable();
35222                 tm.enable.defer(100, tm);
35223             }
35224         }
35225     };
35226     
35227     var getPad = function(){
35228         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35229     };
35230
35231     var show = function(o){
35232         if(disabled){
35233             return;
35234         }
35235         clearTimeout(dismissProc);
35236         ce = o;
35237         if(removeCls){ // in case manually hidden
35238             el.removeClass(removeCls);
35239             removeCls = null;
35240         }
35241         if(ce.cls){
35242             el.addClass(ce.cls);
35243             removeCls = ce.cls;
35244         }
35245         if(ce.title){
35246             tipTitle.update(ce.title);
35247             tipTitle.show();
35248         }else{
35249             tipTitle.update('');
35250             tipTitle.hide();
35251         }
35252         el.dom.style.width  = tm.maxWidth+'px';
35253         //tipBody.dom.style.width = '';
35254         tipBodyText.update(o.text);
35255         var p = getPad(), w = ce.width;
35256         if(!w){
35257             var td = tipBodyText.dom;
35258             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35259             if(aw > tm.maxWidth){
35260                 w = tm.maxWidth;
35261             }else if(aw < tm.minWidth){
35262                 w = tm.minWidth;
35263             }else{
35264                 w = aw;
35265             }
35266         }
35267         //tipBody.setWidth(w);
35268         el.setWidth(parseInt(w, 10) + p);
35269         if(ce.autoHide === false){
35270             close.setDisplayed(true);
35271             if(dd){
35272                 dd.unlock();
35273             }
35274         }else{
35275             close.setDisplayed(false);
35276             if(dd){
35277                 dd.lock();
35278             }
35279         }
35280         if(xy){
35281             el.avoidY = xy[1]-18;
35282             el.setXY(xy);
35283         }
35284         if(tm.animate){
35285             el.setOpacity(.1);
35286             el.setStyle("visibility", "visible");
35287             el.fadeIn({callback: afterShow});
35288         }else{
35289             afterShow();
35290         }
35291     };
35292     
35293     var afterShow = function(){
35294         if(ce){
35295             el.show();
35296             esc.enable();
35297             if(tm.autoDismiss && ce.autoHide !== false){
35298                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35299             }
35300         }
35301     };
35302     
35303     var hide = function(noanim){
35304         clearTimeout(dismissProc);
35305         clearTimeout(hideProc);
35306         ce = null;
35307         if(el.isVisible()){
35308             esc.disable();
35309             if(noanim !== true && tm.animate){
35310                 el.fadeOut({callback: afterHide});
35311             }else{
35312                 afterHide();
35313             } 
35314         }
35315     };
35316     
35317     var afterHide = function(){
35318         el.hide();
35319         if(removeCls){
35320             el.removeClass(removeCls);
35321             removeCls = null;
35322         }
35323     };
35324     
35325     return {
35326         /**
35327         * @cfg {Number} minWidth
35328         * The minimum width of the quick tip (defaults to 40)
35329         */
35330        minWidth : 40,
35331         /**
35332         * @cfg {Number} maxWidth
35333         * The maximum width of the quick tip (defaults to 300)
35334         */
35335        maxWidth : 300,
35336         /**
35337         * @cfg {Boolean} interceptTitles
35338         * True to automatically use the element's DOM title value if available (defaults to false)
35339         */
35340        interceptTitles : false,
35341         /**
35342         * @cfg {Boolean} trackMouse
35343         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35344         */
35345        trackMouse : false,
35346         /**
35347         * @cfg {Boolean} hideOnClick
35348         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35349         */
35350        hideOnClick : true,
35351         /**
35352         * @cfg {Number} showDelay
35353         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35354         */
35355        showDelay : 500,
35356         /**
35357         * @cfg {Number} hideDelay
35358         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35359         */
35360        hideDelay : 200,
35361         /**
35362         * @cfg {Boolean} autoHide
35363         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35364         * Used in conjunction with hideDelay.
35365         */
35366        autoHide : true,
35367         /**
35368         * @cfg {Boolean}
35369         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35370         * (defaults to true).  Used in conjunction with autoDismissDelay.
35371         */
35372        autoDismiss : true,
35373         /**
35374         * @cfg {Number}
35375         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35376         */
35377        autoDismissDelay : 5000,
35378        /**
35379         * @cfg {Boolean} animate
35380         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35381         */
35382        animate : false,
35383
35384        /**
35385         * @cfg {String} title
35386         * Title text to display (defaults to '').  This can be any valid HTML markup.
35387         */
35388         title: '',
35389        /**
35390         * @cfg {String} text
35391         * Body text to display (defaults to '').  This can be any valid HTML markup.
35392         */
35393         text : '',
35394        /**
35395         * @cfg {String} cls
35396         * A CSS class to apply to the base quick tip element (defaults to '').
35397         */
35398         cls : '',
35399        /**
35400         * @cfg {Number} width
35401         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35402         * minWidth or maxWidth.
35403         */
35404         width : null,
35405
35406     /**
35407      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35408      * or display QuickTips in a page.
35409      */
35410        init : function(){
35411           tm = Roo.QuickTips;
35412           cfg = tm.tagConfig;
35413           if(!inited){
35414               if(!Roo.isReady){ // allow calling of init() before onReady
35415                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35416                   return;
35417               }
35418               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35419               el.fxDefaults = {stopFx: true};
35420               // maximum custom styling
35421               //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>');
35422               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>');              
35423               tipTitle = el.child('h3');
35424               tipTitle.enableDisplayMode("block");
35425               tipBody = el.child('div.x-tip-bd');
35426               tipBodyText = el.child('div.x-tip-bd-inner');
35427               //bdLeft = el.child('div.x-tip-bd-left');
35428               //bdRight = el.child('div.x-tip-bd-right');
35429               close = el.child('div.x-tip-close');
35430               close.enableDisplayMode("block");
35431               close.on("click", hide);
35432               var d = Roo.get(document);
35433               d.on("mousedown", onDown);
35434               d.on("mouseover", onOver);
35435               d.on("mouseout", onOut);
35436               d.on("mousemove", onMove);
35437               esc = d.addKeyListener(27, hide);
35438               esc.disable();
35439               if(Roo.dd.DD){
35440                   dd = el.initDD("default", null, {
35441                       onDrag : function(){
35442                           el.sync();  
35443                       }
35444                   });
35445                   dd.setHandleElId(tipTitle.id);
35446                   dd.lock();
35447               }
35448               inited = true;
35449           }
35450           this.enable(); 
35451        },
35452
35453     /**
35454      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35455      * are supported:
35456      * <pre>
35457 Property    Type                   Description
35458 ----------  ---------------------  ------------------------------------------------------------------------
35459 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35460      * </ul>
35461      * @param {Object} config The config object
35462      */
35463        register : function(config){
35464            var cs = config instanceof Array ? config : arguments;
35465            for(var i = 0, len = cs.length; i < len; i++) {
35466                var c = cs[i];
35467                var target = c.target;
35468                if(target){
35469                    if(target instanceof Array){
35470                        for(var j = 0, jlen = target.length; j < jlen; j++){
35471                            tagEls[target[j]] = c;
35472                        }
35473                    }else{
35474                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35475                    }
35476                }
35477            }
35478        },
35479
35480     /**
35481      * Removes this quick tip from its element and destroys it.
35482      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35483      */
35484        unregister : function(el){
35485            delete tagEls[Roo.id(el)];
35486        },
35487
35488     /**
35489      * Enable this quick tip.
35490      */
35491        enable : function(){
35492            if(inited && disabled){
35493                locks.pop();
35494                if(locks.length < 1){
35495                    disabled = false;
35496                }
35497            }
35498        },
35499
35500     /**
35501      * Disable this quick tip.
35502      */
35503        disable : function(){
35504           disabled = true;
35505           clearTimeout(showProc);
35506           clearTimeout(hideProc);
35507           clearTimeout(dismissProc);
35508           if(ce){
35509               hide(true);
35510           }
35511           locks.push(1);
35512        },
35513
35514     /**
35515      * Returns true if the quick tip is enabled, else false.
35516      */
35517        isEnabled : function(){
35518             return !disabled;
35519        },
35520
35521         // private
35522        tagConfig : {
35523            namespace : "roo", // was ext?? this may break..
35524            alt_namespace : "ext",
35525            attribute : "qtip",
35526            width : "width",
35527            target : "target",
35528            title : "qtitle",
35529            hide : "hide",
35530            cls : "qclass"
35531        }
35532    };
35533 }();
35534
35535 // backwards compat
35536 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35537  * Based on:
35538  * Ext JS Library 1.1.1
35539  * Copyright(c) 2006-2007, Ext JS, LLC.
35540  *
35541  * Originally Released Under LGPL - original licence link has changed is not relivant.
35542  *
35543  * Fork - LGPL
35544  * <script type="text/javascript">
35545  */
35546  
35547
35548 /**
35549  * @class Roo.tree.TreePanel
35550  * @extends Roo.data.Tree
35551  * @cfg {Roo.tree.TreeNode} root The root node
35552  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35553  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35554  * @cfg {Boolean} enableDD true to enable drag and drop
35555  * @cfg {Boolean} enableDrag true to enable just drag
35556  * @cfg {Boolean} enableDrop true to enable just drop
35557  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35558  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35559  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35560  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35561  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35562  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35563  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35564  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35565  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35566  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35567  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35568  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35569  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35570  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35571  * @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>
35572  * @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>
35573  * 
35574  * @constructor
35575  * @param {String/HTMLElement/Element} el The container element
35576  * @param {Object} config
35577  */
35578 Roo.tree.TreePanel = function(el, config){
35579     var root = false;
35580     var loader = false;
35581     if (config.root) {
35582         root = config.root;
35583         delete config.root;
35584     }
35585     if (config.loader) {
35586         loader = config.loader;
35587         delete config.loader;
35588     }
35589     
35590     Roo.apply(this, config);
35591     Roo.tree.TreePanel.superclass.constructor.call(this);
35592     this.el = Roo.get(el);
35593     this.el.addClass('x-tree');
35594     //console.log(root);
35595     if (root) {
35596         this.setRootNode( Roo.factory(root, Roo.tree));
35597     }
35598     if (loader) {
35599         this.loader = Roo.factory(loader, Roo.tree);
35600     }
35601    /**
35602     * Read-only. The id of the container element becomes this TreePanel's id.
35603     */
35604     this.id = this.el.id;
35605     this.addEvents({
35606         /**
35607         * @event beforeload
35608         * Fires before a node is loaded, return false to cancel
35609         * @param {Node} node The node being loaded
35610         */
35611         "beforeload" : true,
35612         /**
35613         * @event load
35614         * Fires when a node is loaded
35615         * @param {Node} node The node that was loaded
35616         */
35617         "load" : true,
35618         /**
35619         * @event textchange
35620         * Fires when the text for a node is changed
35621         * @param {Node} node The node
35622         * @param {String} text The new text
35623         * @param {String} oldText The old text
35624         */
35625         "textchange" : true,
35626         /**
35627         * @event beforeexpand
35628         * Fires before a node is expanded, return false to cancel.
35629         * @param {Node} node The node
35630         * @param {Boolean} deep
35631         * @param {Boolean} anim
35632         */
35633         "beforeexpand" : true,
35634         /**
35635         * @event beforecollapse
35636         * Fires before a node is collapsed, return false to cancel.
35637         * @param {Node} node The node
35638         * @param {Boolean} deep
35639         * @param {Boolean} anim
35640         */
35641         "beforecollapse" : true,
35642         /**
35643         * @event expand
35644         * Fires when a node is expanded
35645         * @param {Node} node The node
35646         */
35647         "expand" : true,
35648         /**
35649         * @event disabledchange
35650         * Fires when the disabled status of a node changes
35651         * @param {Node} node The node
35652         * @param {Boolean} disabled
35653         */
35654         "disabledchange" : true,
35655         /**
35656         * @event collapse
35657         * Fires when a node is collapsed
35658         * @param {Node} node The node
35659         */
35660         "collapse" : true,
35661         /**
35662         * @event beforeclick
35663         * Fires before click processing on a node. Return false to cancel the default action.
35664         * @param {Node} node The node
35665         * @param {Roo.EventObject} e The event object
35666         */
35667         "beforeclick":true,
35668         /**
35669         * @event checkchange
35670         * Fires when a node with a checkbox's checked property changes
35671         * @param {Node} this This node
35672         * @param {Boolean} checked
35673         */
35674         "checkchange":true,
35675         /**
35676         * @event click
35677         * Fires when a node is clicked
35678         * @param {Node} node The node
35679         * @param {Roo.EventObject} e The event object
35680         */
35681         "click":true,
35682         /**
35683         * @event dblclick
35684         * Fires when a node is double clicked
35685         * @param {Node} node The node
35686         * @param {Roo.EventObject} e The event object
35687         */
35688         "dblclick":true,
35689         /**
35690         * @event contextmenu
35691         * Fires when a node is right clicked
35692         * @param {Node} node The node
35693         * @param {Roo.EventObject} e The event object
35694         */
35695         "contextmenu":true,
35696         /**
35697         * @event beforechildrenrendered
35698         * Fires right before the child nodes for a node are rendered
35699         * @param {Node} node The node
35700         */
35701         "beforechildrenrendered":true,
35702         /**
35703         * @event startdrag
35704         * Fires when a node starts being dragged
35705         * @param {Roo.tree.TreePanel} this
35706         * @param {Roo.tree.TreeNode} node
35707         * @param {event} e The raw browser event
35708         */ 
35709        "startdrag" : true,
35710        /**
35711         * @event enddrag
35712         * Fires when a drag operation is complete
35713         * @param {Roo.tree.TreePanel} this
35714         * @param {Roo.tree.TreeNode} node
35715         * @param {event} e The raw browser event
35716         */
35717        "enddrag" : true,
35718        /**
35719         * @event dragdrop
35720         * Fires when a dragged node is dropped on a valid DD target
35721         * @param {Roo.tree.TreePanel} this
35722         * @param {Roo.tree.TreeNode} node
35723         * @param {DD} dd The dd it was dropped on
35724         * @param {event} e The raw browser event
35725         */
35726        "dragdrop" : true,
35727        /**
35728         * @event beforenodedrop
35729         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35730         * passed to handlers has the following properties:<br />
35731         * <ul style="padding:5px;padding-left:16px;">
35732         * <li>tree - The TreePanel</li>
35733         * <li>target - The node being targeted for the drop</li>
35734         * <li>data - The drag data from the drag source</li>
35735         * <li>point - The point of the drop - append, above or below</li>
35736         * <li>source - The drag source</li>
35737         * <li>rawEvent - Raw mouse event</li>
35738         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35739         * to be inserted by setting them on this object.</li>
35740         * <li>cancel - Set this to true to cancel the drop.</li>
35741         * </ul>
35742         * @param {Object} dropEvent
35743         */
35744        "beforenodedrop" : true,
35745        /**
35746         * @event nodedrop
35747         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35748         * passed to handlers has the following properties:<br />
35749         * <ul style="padding:5px;padding-left:16px;">
35750         * <li>tree - The TreePanel</li>
35751         * <li>target - The node being targeted for the drop</li>
35752         * <li>data - The drag data from the drag source</li>
35753         * <li>point - The point of the drop - append, above or below</li>
35754         * <li>source - The drag source</li>
35755         * <li>rawEvent - Raw mouse event</li>
35756         * <li>dropNode - Dropped node(s).</li>
35757         * </ul>
35758         * @param {Object} dropEvent
35759         */
35760        "nodedrop" : true,
35761         /**
35762         * @event nodedragover
35763         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35764         * passed to handlers has the following properties:<br />
35765         * <ul style="padding:5px;padding-left:16px;">
35766         * <li>tree - The TreePanel</li>
35767         * <li>target - The node being targeted for the drop</li>
35768         * <li>data - The drag data from the drag source</li>
35769         * <li>point - The point of the drop - append, above or below</li>
35770         * <li>source - The drag source</li>
35771         * <li>rawEvent - Raw mouse event</li>
35772         * <li>dropNode - Drop node(s) provided by the source.</li>
35773         * <li>cancel - Set this to true to signal drop not allowed.</li>
35774         * </ul>
35775         * @param {Object} dragOverEvent
35776         */
35777        "nodedragover" : true,
35778        /**
35779         * @event appendnode
35780         * Fires when append node to the tree
35781         * @param {Roo.tree.TreePanel} this
35782         * @param {Roo.tree.TreeNode} node
35783         * @param {Number} index The index of the newly appended node
35784         */
35785        "appendnode" : true
35786         
35787     });
35788     if(this.singleExpand){
35789        this.on("beforeexpand", this.restrictExpand, this);
35790     }
35791     if (this.editor) {
35792         this.editor.tree = this;
35793         this.editor = Roo.factory(this.editor, Roo.tree);
35794     }
35795     
35796     if (this.selModel) {
35797         this.selModel = Roo.factory(this.selModel, Roo.tree);
35798     }
35799    
35800 };
35801 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35802     rootVisible : true,
35803     animate: Roo.enableFx,
35804     lines : true,
35805     enableDD : false,
35806     hlDrop : Roo.enableFx,
35807   
35808     renderer: false,
35809     
35810     rendererTip: false,
35811     // private
35812     restrictExpand : function(node){
35813         var p = node.parentNode;
35814         if(p){
35815             if(p.expandedChild && p.expandedChild.parentNode == p){
35816                 p.expandedChild.collapse();
35817             }
35818             p.expandedChild = node;
35819         }
35820     },
35821
35822     // private override
35823     setRootNode : function(node){
35824         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35825         if(!this.rootVisible){
35826             node.ui = new Roo.tree.RootTreeNodeUI(node);
35827         }
35828         return node;
35829     },
35830
35831     /**
35832      * Returns the container element for this TreePanel
35833      */
35834     getEl : function(){
35835         return this.el;
35836     },
35837
35838     /**
35839      * Returns the default TreeLoader for this TreePanel
35840      */
35841     getLoader : function(){
35842         return this.loader;
35843     },
35844
35845     /**
35846      * Expand all nodes
35847      */
35848     expandAll : function(){
35849         this.root.expand(true);
35850     },
35851
35852     /**
35853      * Collapse all nodes
35854      */
35855     collapseAll : function(){
35856         this.root.collapse(true);
35857     },
35858
35859     /**
35860      * Returns the selection model used by this TreePanel
35861      */
35862     getSelectionModel : function(){
35863         if(!this.selModel){
35864             this.selModel = new Roo.tree.DefaultSelectionModel();
35865         }
35866         return this.selModel;
35867     },
35868
35869     /**
35870      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35871      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35872      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35873      * @return {Array}
35874      */
35875     getChecked : function(a, startNode){
35876         startNode = startNode || this.root;
35877         var r = [];
35878         var f = function(){
35879             if(this.attributes.checked){
35880                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35881             }
35882         }
35883         startNode.cascade(f);
35884         return r;
35885     },
35886
35887     /**
35888      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35889      * @param {String} path
35890      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35891      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35892      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35893      */
35894     expandPath : function(path, attr, callback){
35895         attr = attr || "id";
35896         var keys = path.split(this.pathSeparator);
35897         var curNode = this.root;
35898         if(curNode.attributes[attr] != keys[1]){ // invalid root
35899             if(callback){
35900                 callback(false, null);
35901             }
35902             return;
35903         }
35904         var index = 1;
35905         var f = function(){
35906             if(++index == keys.length){
35907                 if(callback){
35908                     callback(true, curNode);
35909                 }
35910                 return;
35911             }
35912             var c = curNode.findChild(attr, keys[index]);
35913             if(!c){
35914                 if(callback){
35915                     callback(false, curNode);
35916                 }
35917                 return;
35918             }
35919             curNode = c;
35920             c.expand(false, false, f);
35921         };
35922         curNode.expand(false, false, f);
35923     },
35924
35925     /**
35926      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35927      * @param {String} path
35928      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35929      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35930      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35931      */
35932     selectPath : function(path, attr, callback){
35933         attr = attr || "id";
35934         var keys = path.split(this.pathSeparator);
35935         var v = keys.pop();
35936         if(keys.length > 0){
35937             var f = function(success, node){
35938                 if(success && node){
35939                     var n = node.findChild(attr, v);
35940                     if(n){
35941                         n.select();
35942                         if(callback){
35943                             callback(true, n);
35944                         }
35945                     }else if(callback){
35946                         callback(false, n);
35947                     }
35948                 }else{
35949                     if(callback){
35950                         callback(false, n);
35951                     }
35952                 }
35953             };
35954             this.expandPath(keys.join(this.pathSeparator), attr, f);
35955         }else{
35956             this.root.select();
35957             if(callback){
35958                 callback(true, this.root);
35959             }
35960         }
35961     },
35962
35963     getTreeEl : function(){
35964         return this.el;
35965     },
35966
35967     /**
35968      * Trigger rendering of this TreePanel
35969      */
35970     render : function(){
35971         if (this.innerCt) {
35972             return this; // stop it rendering more than once!!
35973         }
35974         
35975         this.innerCt = this.el.createChild({tag:"ul",
35976                cls:"x-tree-root-ct " +
35977                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35978
35979         if(this.containerScroll){
35980             Roo.dd.ScrollManager.register(this.el);
35981         }
35982         if((this.enableDD || this.enableDrop) && !this.dropZone){
35983            /**
35984             * The dropZone used by this tree if drop is enabled
35985             * @type Roo.tree.TreeDropZone
35986             */
35987              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35988                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35989            });
35990         }
35991         if((this.enableDD || this.enableDrag) && !this.dragZone){
35992            /**
35993             * The dragZone used by this tree if drag is enabled
35994             * @type Roo.tree.TreeDragZone
35995             */
35996             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35997                ddGroup: this.ddGroup || "TreeDD",
35998                scroll: this.ddScroll
35999            });
36000         }
36001         this.getSelectionModel().init(this);
36002         if (!this.root) {
36003             Roo.log("ROOT not set in tree");
36004             return this;
36005         }
36006         this.root.render();
36007         if(!this.rootVisible){
36008             this.root.renderChildren();
36009         }
36010         return this;
36011     }
36012 });/*
36013  * Based on:
36014  * Ext JS Library 1.1.1
36015  * Copyright(c) 2006-2007, Ext JS, LLC.
36016  *
36017  * Originally Released Under LGPL - original licence link has changed is not relivant.
36018  *
36019  * Fork - LGPL
36020  * <script type="text/javascript">
36021  */
36022  
36023
36024 /**
36025  * @class Roo.tree.DefaultSelectionModel
36026  * @extends Roo.util.Observable
36027  * The default single selection for a TreePanel.
36028  * @param {Object} cfg Configuration
36029  */
36030 Roo.tree.DefaultSelectionModel = function(cfg){
36031    this.selNode = null;
36032    
36033    
36034    
36035    this.addEvents({
36036        /**
36037         * @event selectionchange
36038         * Fires when the selected node changes
36039         * @param {DefaultSelectionModel} this
36040         * @param {TreeNode} node the new selection
36041         */
36042        "selectionchange" : true,
36043
36044        /**
36045         * @event beforeselect
36046         * Fires before the selected node changes, return false to cancel the change
36047         * @param {DefaultSelectionModel} this
36048         * @param {TreeNode} node the new selection
36049         * @param {TreeNode} node the old selection
36050         */
36051        "beforeselect" : true
36052    });
36053    
36054     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36055 };
36056
36057 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36058     init : function(tree){
36059         this.tree = tree;
36060         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36061         tree.on("click", this.onNodeClick, this);
36062     },
36063     
36064     onNodeClick : function(node, e){
36065         if (e.ctrlKey && this.selNode == node)  {
36066             this.unselect(node);
36067             return;
36068         }
36069         this.select(node);
36070     },
36071     
36072     /**
36073      * Select a node.
36074      * @param {TreeNode} node The node to select
36075      * @return {TreeNode} The selected node
36076      */
36077     select : function(node){
36078         var last = this.selNode;
36079         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36080             if(last){
36081                 last.ui.onSelectedChange(false);
36082             }
36083             this.selNode = node;
36084             node.ui.onSelectedChange(true);
36085             this.fireEvent("selectionchange", this, node, last);
36086         }
36087         return node;
36088     },
36089     
36090     /**
36091      * Deselect a node.
36092      * @param {TreeNode} node The node to unselect
36093      */
36094     unselect : function(node){
36095         if(this.selNode == node){
36096             this.clearSelections();
36097         }    
36098     },
36099     
36100     /**
36101      * Clear all selections
36102      */
36103     clearSelections : function(){
36104         var n = this.selNode;
36105         if(n){
36106             n.ui.onSelectedChange(false);
36107             this.selNode = null;
36108             this.fireEvent("selectionchange", this, null);
36109         }
36110         return n;
36111     },
36112     
36113     /**
36114      * Get the selected node
36115      * @return {TreeNode} The selected node
36116      */
36117     getSelectedNode : function(){
36118         return this.selNode;    
36119     },
36120     
36121     /**
36122      * Returns true if the node is selected
36123      * @param {TreeNode} node The node to check
36124      * @return {Boolean}
36125      */
36126     isSelected : function(node){
36127         return this.selNode == node;  
36128     },
36129
36130     /**
36131      * Selects the node above the selected node in the tree, intelligently walking the nodes
36132      * @return TreeNode The new selection
36133      */
36134     selectPrevious : function(){
36135         var s = this.selNode || this.lastSelNode;
36136         if(!s){
36137             return null;
36138         }
36139         var ps = s.previousSibling;
36140         if(ps){
36141             if(!ps.isExpanded() || ps.childNodes.length < 1){
36142                 return this.select(ps);
36143             } else{
36144                 var lc = ps.lastChild;
36145                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36146                     lc = lc.lastChild;
36147                 }
36148                 return this.select(lc);
36149             }
36150         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36151             return this.select(s.parentNode);
36152         }
36153         return null;
36154     },
36155
36156     /**
36157      * Selects the node above the selected node in the tree, intelligently walking the nodes
36158      * @return TreeNode The new selection
36159      */
36160     selectNext : function(){
36161         var s = this.selNode || this.lastSelNode;
36162         if(!s){
36163             return null;
36164         }
36165         if(s.firstChild && s.isExpanded()){
36166              return this.select(s.firstChild);
36167          }else if(s.nextSibling){
36168              return this.select(s.nextSibling);
36169          }else if(s.parentNode){
36170             var newS = null;
36171             s.parentNode.bubble(function(){
36172                 if(this.nextSibling){
36173                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36174                     return false;
36175                 }
36176             });
36177             return newS;
36178          }
36179         return null;
36180     },
36181
36182     onKeyDown : function(e){
36183         var s = this.selNode || this.lastSelNode;
36184         // undesirable, but required
36185         var sm = this;
36186         if(!s){
36187             return;
36188         }
36189         var k = e.getKey();
36190         switch(k){
36191              case e.DOWN:
36192                  e.stopEvent();
36193                  this.selectNext();
36194              break;
36195              case e.UP:
36196                  e.stopEvent();
36197                  this.selectPrevious();
36198              break;
36199              case e.RIGHT:
36200                  e.preventDefault();
36201                  if(s.hasChildNodes()){
36202                      if(!s.isExpanded()){
36203                          s.expand();
36204                      }else if(s.firstChild){
36205                          this.select(s.firstChild, e);
36206                      }
36207                  }
36208              break;
36209              case e.LEFT:
36210                  e.preventDefault();
36211                  if(s.hasChildNodes() && s.isExpanded()){
36212                      s.collapse();
36213                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36214                      this.select(s.parentNode, e);
36215                  }
36216              break;
36217         };
36218     }
36219 });
36220
36221 /**
36222  * @class Roo.tree.MultiSelectionModel
36223  * @extends Roo.util.Observable
36224  * Multi selection for a TreePanel.
36225  * @param {Object} cfg Configuration
36226  */
36227 Roo.tree.MultiSelectionModel = function(){
36228    this.selNodes = [];
36229    this.selMap = {};
36230    this.addEvents({
36231        /**
36232         * @event selectionchange
36233         * Fires when the selected nodes change
36234         * @param {MultiSelectionModel} this
36235         * @param {Array} nodes Array of the selected nodes
36236         */
36237        "selectionchange" : true
36238    });
36239    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36240    
36241 };
36242
36243 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36244     init : function(tree){
36245         this.tree = tree;
36246         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36247         tree.on("click", this.onNodeClick, this);
36248     },
36249     
36250     onNodeClick : function(node, e){
36251         this.select(node, e, e.ctrlKey);
36252     },
36253     
36254     /**
36255      * Select a node.
36256      * @param {TreeNode} node The node to select
36257      * @param {EventObject} e (optional) An event associated with the selection
36258      * @param {Boolean} keepExisting True to retain existing selections
36259      * @return {TreeNode} The selected node
36260      */
36261     select : function(node, e, keepExisting){
36262         if(keepExisting !== true){
36263             this.clearSelections(true);
36264         }
36265         if(this.isSelected(node)){
36266             this.lastSelNode = node;
36267             return node;
36268         }
36269         this.selNodes.push(node);
36270         this.selMap[node.id] = node;
36271         this.lastSelNode = node;
36272         node.ui.onSelectedChange(true);
36273         this.fireEvent("selectionchange", this, this.selNodes);
36274         return node;
36275     },
36276     
36277     /**
36278      * Deselect a node.
36279      * @param {TreeNode} node The node to unselect
36280      */
36281     unselect : function(node){
36282         if(this.selMap[node.id]){
36283             node.ui.onSelectedChange(false);
36284             var sn = this.selNodes;
36285             var index = -1;
36286             if(sn.indexOf){
36287                 index = sn.indexOf(node);
36288             }else{
36289                 for(var i = 0, len = sn.length; i < len; i++){
36290                     if(sn[i] == node){
36291                         index = i;
36292                         break;
36293                     }
36294                 }
36295             }
36296             if(index != -1){
36297                 this.selNodes.splice(index, 1);
36298             }
36299             delete this.selMap[node.id];
36300             this.fireEvent("selectionchange", this, this.selNodes);
36301         }
36302     },
36303     
36304     /**
36305      * Clear all selections
36306      */
36307     clearSelections : function(suppressEvent){
36308         var sn = this.selNodes;
36309         if(sn.length > 0){
36310             for(var i = 0, len = sn.length; i < len; i++){
36311                 sn[i].ui.onSelectedChange(false);
36312             }
36313             this.selNodes = [];
36314             this.selMap = {};
36315             if(suppressEvent !== true){
36316                 this.fireEvent("selectionchange", this, this.selNodes);
36317             }
36318         }
36319     },
36320     
36321     /**
36322      * Returns true if the node is selected
36323      * @param {TreeNode} node The node to check
36324      * @return {Boolean}
36325      */
36326     isSelected : function(node){
36327         return this.selMap[node.id] ? true : false;  
36328     },
36329     
36330     /**
36331      * Returns an array of the selected nodes
36332      * @return {Array}
36333      */
36334     getSelectedNodes : function(){
36335         return this.selNodes;    
36336     },
36337
36338     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36339
36340     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36341
36342     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36343 });/*
36344  * Based on:
36345  * Ext JS Library 1.1.1
36346  * Copyright(c) 2006-2007, Ext JS, LLC.
36347  *
36348  * Originally Released Under LGPL - original licence link has changed is not relivant.
36349  *
36350  * Fork - LGPL
36351  * <script type="text/javascript">
36352  */
36353  
36354 /**
36355  * @class Roo.tree.TreeNode
36356  * @extends Roo.data.Node
36357  * @cfg {String} text The text for this node
36358  * @cfg {Boolean} expanded true to start the node expanded
36359  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36360  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36361  * @cfg {Boolean} disabled true to start the node disabled
36362  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36363  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36364  * @cfg {String} cls A css class to be added to the node
36365  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36366  * @cfg {String} href URL of the link used for the node (defaults to #)
36367  * @cfg {String} hrefTarget target frame for the link
36368  * @cfg {String} qtip An Ext QuickTip for the node
36369  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36370  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36371  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36372  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36373  * (defaults to undefined with no checkbox rendered)
36374  * @constructor
36375  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36376  */
36377 Roo.tree.TreeNode = function(attributes){
36378     attributes = attributes || {};
36379     if(typeof attributes == "string"){
36380         attributes = {text: attributes};
36381     }
36382     this.childrenRendered = false;
36383     this.rendered = false;
36384     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36385     this.expanded = attributes.expanded === true;
36386     this.isTarget = attributes.isTarget !== false;
36387     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36388     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36389
36390     /**
36391      * Read-only. The text for this node. To change it use setText().
36392      * @type String
36393      */
36394     this.text = attributes.text;
36395     /**
36396      * True if this node is disabled.
36397      * @type Boolean
36398      */
36399     this.disabled = attributes.disabled === true;
36400
36401     this.addEvents({
36402         /**
36403         * @event textchange
36404         * Fires when the text for this node is changed
36405         * @param {Node} this This node
36406         * @param {String} text The new text
36407         * @param {String} oldText The old text
36408         */
36409         "textchange" : true,
36410         /**
36411         * @event beforeexpand
36412         * Fires before this node is expanded, return false to cancel.
36413         * @param {Node} this This node
36414         * @param {Boolean} deep
36415         * @param {Boolean} anim
36416         */
36417         "beforeexpand" : true,
36418         /**
36419         * @event beforecollapse
36420         * Fires before this node is collapsed, return false to cancel.
36421         * @param {Node} this This node
36422         * @param {Boolean} deep
36423         * @param {Boolean} anim
36424         */
36425         "beforecollapse" : true,
36426         /**
36427         * @event expand
36428         * Fires when this node is expanded
36429         * @param {Node} this This node
36430         */
36431         "expand" : true,
36432         /**
36433         * @event disabledchange
36434         * Fires when the disabled status of this node changes
36435         * @param {Node} this This node
36436         * @param {Boolean} disabled
36437         */
36438         "disabledchange" : true,
36439         /**
36440         * @event collapse
36441         * Fires when this node is collapsed
36442         * @param {Node} this This node
36443         */
36444         "collapse" : true,
36445         /**
36446         * @event beforeclick
36447         * Fires before click processing. Return false to cancel the default action.
36448         * @param {Node} this This node
36449         * @param {Roo.EventObject} e The event object
36450         */
36451         "beforeclick":true,
36452         /**
36453         * @event checkchange
36454         * Fires when a node with a checkbox's checked property changes
36455         * @param {Node} this This node
36456         * @param {Boolean} checked
36457         */
36458         "checkchange":true,
36459         /**
36460         * @event click
36461         * Fires when this node is clicked
36462         * @param {Node} this This node
36463         * @param {Roo.EventObject} e The event object
36464         */
36465         "click":true,
36466         /**
36467         * @event dblclick
36468         * Fires when this node is double clicked
36469         * @param {Node} this This node
36470         * @param {Roo.EventObject} e The event object
36471         */
36472         "dblclick":true,
36473         /**
36474         * @event contextmenu
36475         * Fires when this node is right clicked
36476         * @param {Node} this This node
36477         * @param {Roo.EventObject} e The event object
36478         */
36479         "contextmenu":true,
36480         /**
36481         * @event beforechildrenrendered
36482         * Fires right before the child nodes for this node are rendered
36483         * @param {Node} this This node
36484         */
36485         "beforechildrenrendered":true
36486     });
36487
36488     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36489
36490     /**
36491      * Read-only. The UI for this node
36492      * @type TreeNodeUI
36493      */
36494     this.ui = new uiClass(this);
36495     
36496     // finally support items[]
36497     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36498         return;
36499     }
36500     
36501     
36502     Roo.each(this.attributes.items, function(c) {
36503         this.appendChild(Roo.factory(c,Roo.Tree));
36504     }, this);
36505     delete this.attributes.items;
36506     
36507     
36508     
36509 };
36510 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36511     preventHScroll: true,
36512     /**
36513      * Returns true if this node is expanded
36514      * @return {Boolean}
36515      */
36516     isExpanded : function(){
36517         return this.expanded;
36518     },
36519
36520     /**
36521      * Returns the UI object for this node
36522      * @return {TreeNodeUI}
36523      */
36524     getUI : function(){
36525         return this.ui;
36526     },
36527
36528     // private override
36529     setFirstChild : function(node){
36530         var of = this.firstChild;
36531         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36532         if(this.childrenRendered && of && node != of){
36533             of.renderIndent(true, true);
36534         }
36535         if(this.rendered){
36536             this.renderIndent(true, true);
36537         }
36538     },
36539
36540     // private override
36541     setLastChild : function(node){
36542         var ol = this.lastChild;
36543         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36544         if(this.childrenRendered && ol && node != ol){
36545             ol.renderIndent(true, true);
36546         }
36547         if(this.rendered){
36548             this.renderIndent(true, true);
36549         }
36550     },
36551
36552     // these methods are overridden to provide lazy rendering support
36553     // private override
36554     appendChild : function()
36555     {
36556         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36557         if(node && this.childrenRendered){
36558             node.render();
36559         }
36560         this.ui.updateExpandIcon();
36561         return node;
36562     },
36563
36564     // private override
36565     removeChild : function(node){
36566         this.ownerTree.getSelectionModel().unselect(node);
36567         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36568         // if it's been rendered remove dom node
36569         if(this.childrenRendered){
36570             node.ui.remove();
36571         }
36572         if(this.childNodes.length < 1){
36573             this.collapse(false, false);
36574         }else{
36575             this.ui.updateExpandIcon();
36576         }
36577         if(!this.firstChild) {
36578             this.childrenRendered = false;
36579         }
36580         return node;
36581     },
36582
36583     // private override
36584     insertBefore : function(node, refNode){
36585         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36586         if(newNode && refNode && this.childrenRendered){
36587             node.render();
36588         }
36589         this.ui.updateExpandIcon();
36590         return newNode;
36591     },
36592
36593     /**
36594      * Sets the text for this node
36595      * @param {String} text
36596      */
36597     setText : function(text){
36598         var oldText = this.text;
36599         this.text = text;
36600         this.attributes.text = text;
36601         if(this.rendered){ // event without subscribing
36602             this.ui.onTextChange(this, text, oldText);
36603         }
36604         this.fireEvent("textchange", this, text, oldText);
36605     },
36606
36607     /**
36608      * Triggers selection of this node
36609      */
36610     select : function(){
36611         this.getOwnerTree().getSelectionModel().select(this);
36612     },
36613
36614     /**
36615      * Triggers deselection of this node
36616      */
36617     unselect : function(){
36618         this.getOwnerTree().getSelectionModel().unselect(this);
36619     },
36620
36621     /**
36622      * Returns true if this node is selected
36623      * @return {Boolean}
36624      */
36625     isSelected : function(){
36626         return this.getOwnerTree().getSelectionModel().isSelected(this);
36627     },
36628
36629     /**
36630      * Expand this node.
36631      * @param {Boolean} deep (optional) True to expand all children as well
36632      * @param {Boolean} anim (optional) false to cancel the default animation
36633      * @param {Function} callback (optional) A callback to be called when
36634      * expanding this node completes (does not wait for deep expand to complete).
36635      * Called with 1 parameter, this node.
36636      */
36637     expand : function(deep, anim, callback){
36638         if(!this.expanded){
36639             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36640                 return;
36641             }
36642             if(!this.childrenRendered){
36643                 this.renderChildren();
36644             }
36645             this.expanded = true;
36646             
36647             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36648                 this.ui.animExpand(function(){
36649                     this.fireEvent("expand", this);
36650                     if(typeof callback == "function"){
36651                         callback(this);
36652                     }
36653                     if(deep === true){
36654                         this.expandChildNodes(true);
36655                     }
36656                 }.createDelegate(this));
36657                 return;
36658             }else{
36659                 this.ui.expand();
36660                 this.fireEvent("expand", this);
36661                 if(typeof callback == "function"){
36662                     callback(this);
36663                 }
36664             }
36665         }else{
36666            if(typeof callback == "function"){
36667                callback(this);
36668            }
36669         }
36670         if(deep === true){
36671             this.expandChildNodes(true);
36672         }
36673     },
36674
36675     isHiddenRoot : function(){
36676         return this.isRoot && !this.getOwnerTree().rootVisible;
36677     },
36678
36679     /**
36680      * Collapse this node.
36681      * @param {Boolean} deep (optional) True to collapse all children as well
36682      * @param {Boolean} anim (optional) false to cancel the default animation
36683      */
36684     collapse : function(deep, anim){
36685         if(this.expanded && !this.isHiddenRoot()){
36686             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36687                 return;
36688             }
36689             this.expanded = false;
36690             if((this.getOwnerTree().animate && anim !== false) || anim){
36691                 this.ui.animCollapse(function(){
36692                     this.fireEvent("collapse", this);
36693                     if(deep === true){
36694                         this.collapseChildNodes(true);
36695                     }
36696                 }.createDelegate(this));
36697                 return;
36698             }else{
36699                 this.ui.collapse();
36700                 this.fireEvent("collapse", this);
36701             }
36702         }
36703         if(deep === true){
36704             var cs = this.childNodes;
36705             for(var i = 0, len = cs.length; i < len; i++) {
36706                 cs[i].collapse(true, false);
36707             }
36708         }
36709     },
36710
36711     // private
36712     delayedExpand : function(delay){
36713         if(!this.expandProcId){
36714             this.expandProcId = this.expand.defer(delay, this);
36715         }
36716     },
36717
36718     // private
36719     cancelExpand : function(){
36720         if(this.expandProcId){
36721             clearTimeout(this.expandProcId);
36722         }
36723         this.expandProcId = false;
36724     },
36725
36726     /**
36727      * Toggles expanded/collapsed state of the node
36728      */
36729     toggle : function(){
36730         if(this.expanded){
36731             this.collapse();
36732         }else{
36733             this.expand();
36734         }
36735     },
36736
36737     /**
36738      * Ensures all parent nodes are expanded
36739      */
36740     ensureVisible : function(callback){
36741         var tree = this.getOwnerTree();
36742         tree.expandPath(this.parentNode.getPath(), false, function(){
36743             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36744             Roo.callback(callback);
36745         }.createDelegate(this));
36746     },
36747
36748     /**
36749      * Expand all child nodes
36750      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36751      */
36752     expandChildNodes : function(deep){
36753         var cs = this.childNodes;
36754         for(var i = 0, len = cs.length; i < len; i++) {
36755                 cs[i].expand(deep);
36756         }
36757     },
36758
36759     /**
36760      * Collapse all child nodes
36761      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36762      */
36763     collapseChildNodes : function(deep){
36764         var cs = this.childNodes;
36765         for(var i = 0, len = cs.length; i < len; i++) {
36766                 cs[i].collapse(deep);
36767         }
36768     },
36769
36770     /**
36771      * Disables this node
36772      */
36773     disable : function(){
36774         this.disabled = true;
36775         this.unselect();
36776         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36777             this.ui.onDisableChange(this, true);
36778         }
36779         this.fireEvent("disabledchange", this, true);
36780     },
36781
36782     /**
36783      * Enables this node
36784      */
36785     enable : function(){
36786         this.disabled = false;
36787         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36788             this.ui.onDisableChange(this, false);
36789         }
36790         this.fireEvent("disabledchange", this, false);
36791     },
36792
36793     // private
36794     renderChildren : function(suppressEvent){
36795         if(suppressEvent !== false){
36796             this.fireEvent("beforechildrenrendered", this);
36797         }
36798         var cs = this.childNodes;
36799         for(var i = 0, len = cs.length; i < len; i++){
36800             cs[i].render(true);
36801         }
36802         this.childrenRendered = true;
36803     },
36804
36805     // private
36806     sort : function(fn, scope){
36807         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36808         if(this.childrenRendered){
36809             var cs = this.childNodes;
36810             for(var i = 0, len = cs.length; i < len; i++){
36811                 cs[i].render(true);
36812             }
36813         }
36814     },
36815
36816     // private
36817     render : function(bulkRender){
36818         this.ui.render(bulkRender);
36819         if(!this.rendered){
36820             this.rendered = true;
36821             if(this.expanded){
36822                 this.expanded = false;
36823                 this.expand(false, false);
36824             }
36825         }
36826     },
36827
36828     // private
36829     renderIndent : function(deep, refresh){
36830         if(refresh){
36831             this.ui.childIndent = null;
36832         }
36833         this.ui.renderIndent();
36834         if(deep === true && this.childrenRendered){
36835             var cs = this.childNodes;
36836             for(var i = 0, len = cs.length; i < len; i++){
36837                 cs[i].renderIndent(true, refresh);
36838             }
36839         }
36840     }
36841 });/*
36842  * Based on:
36843  * Ext JS Library 1.1.1
36844  * Copyright(c) 2006-2007, Ext JS, LLC.
36845  *
36846  * Originally Released Under LGPL - original licence link has changed is not relivant.
36847  *
36848  * Fork - LGPL
36849  * <script type="text/javascript">
36850  */
36851  
36852 /**
36853  * @class Roo.tree.AsyncTreeNode
36854  * @extends Roo.tree.TreeNode
36855  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36856  * @constructor
36857  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36858  */
36859  Roo.tree.AsyncTreeNode = function(config){
36860     this.loaded = false;
36861     this.loading = false;
36862     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36863     /**
36864     * @event beforeload
36865     * Fires before this node is loaded, return false to cancel
36866     * @param {Node} this This node
36867     */
36868     this.addEvents({'beforeload':true, 'load': true});
36869     /**
36870     * @event load
36871     * Fires when this node is loaded
36872     * @param {Node} this This node
36873     */
36874     /**
36875      * The loader used by this node (defaults to using the tree's defined loader)
36876      * @type TreeLoader
36877      * @property loader
36878      */
36879 };
36880 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36881     expand : function(deep, anim, callback){
36882         if(this.loading){ // if an async load is already running, waiting til it's done
36883             var timer;
36884             var f = function(){
36885                 if(!this.loading){ // done loading
36886                     clearInterval(timer);
36887                     this.expand(deep, anim, callback);
36888                 }
36889             }.createDelegate(this);
36890             timer = setInterval(f, 200);
36891             return;
36892         }
36893         if(!this.loaded){
36894             if(this.fireEvent("beforeload", this) === false){
36895                 return;
36896             }
36897             this.loading = true;
36898             this.ui.beforeLoad(this);
36899             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36900             if(loader){
36901                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36902                 return;
36903             }
36904         }
36905         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36906     },
36907     
36908     /**
36909      * Returns true if this node is currently loading
36910      * @return {Boolean}
36911      */
36912     isLoading : function(){
36913         return this.loading;  
36914     },
36915     
36916     loadComplete : function(deep, anim, callback){
36917         this.loading = false;
36918         this.loaded = true;
36919         this.ui.afterLoad(this);
36920         this.fireEvent("load", this);
36921         this.expand(deep, anim, callback);
36922     },
36923     
36924     /**
36925      * Returns true if this node has been loaded
36926      * @return {Boolean}
36927      */
36928     isLoaded : function(){
36929         return this.loaded;
36930     },
36931     
36932     hasChildNodes : function(){
36933         if(!this.isLeaf() && !this.loaded){
36934             return true;
36935         }else{
36936             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36937         }
36938     },
36939
36940     /**
36941      * Trigger a reload for this node
36942      * @param {Function} callback
36943      */
36944     reload : function(callback){
36945         this.collapse(false, false);
36946         while(this.firstChild){
36947             this.removeChild(this.firstChild);
36948         }
36949         this.childrenRendered = false;
36950         this.loaded = false;
36951         if(this.isHiddenRoot()){
36952             this.expanded = false;
36953         }
36954         this.expand(false, false, callback);
36955     }
36956 });/*
36957  * Based on:
36958  * Ext JS Library 1.1.1
36959  * Copyright(c) 2006-2007, Ext JS, LLC.
36960  *
36961  * Originally Released Under LGPL - original licence link has changed is not relivant.
36962  *
36963  * Fork - LGPL
36964  * <script type="text/javascript">
36965  */
36966  
36967 /**
36968  * @class Roo.tree.TreeNodeUI
36969  * @constructor
36970  * @param {Object} node The node to render
36971  * The TreeNode UI implementation is separate from the
36972  * tree implementation. Unless you are customizing the tree UI,
36973  * you should never have to use this directly.
36974  */
36975 Roo.tree.TreeNodeUI = function(node){
36976     this.node = node;
36977     this.rendered = false;
36978     this.animating = false;
36979     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36980 };
36981
36982 Roo.tree.TreeNodeUI.prototype = {
36983     removeChild : function(node){
36984         if(this.rendered){
36985             this.ctNode.removeChild(node.ui.getEl());
36986         }
36987     },
36988
36989     beforeLoad : function(){
36990          this.addClass("x-tree-node-loading");
36991     },
36992
36993     afterLoad : function(){
36994          this.removeClass("x-tree-node-loading");
36995     },
36996
36997     onTextChange : function(node, text, oldText){
36998         if(this.rendered){
36999             this.textNode.innerHTML = text;
37000         }
37001     },
37002
37003     onDisableChange : function(node, state){
37004         this.disabled = state;
37005         if(state){
37006             this.addClass("x-tree-node-disabled");
37007         }else{
37008             this.removeClass("x-tree-node-disabled");
37009         }
37010     },
37011
37012     onSelectedChange : function(state){
37013         if(state){
37014             this.focus();
37015             this.addClass("x-tree-selected");
37016         }else{
37017             //this.blur();
37018             this.removeClass("x-tree-selected");
37019         }
37020     },
37021
37022     onMove : function(tree, node, oldParent, newParent, index, refNode){
37023         this.childIndent = null;
37024         if(this.rendered){
37025             var targetNode = newParent.ui.getContainer();
37026             if(!targetNode){//target not rendered
37027                 this.holder = document.createElement("div");
37028                 this.holder.appendChild(this.wrap);
37029                 return;
37030             }
37031             var insertBefore = refNode ? refNode.ui.getEl() : null;
37032             if(insertBefore){
37033                 targetNode.insertBefore(this.wrap, insertBefore);
37034             }else{
37035                 targetNode.appendChild(this.wrap);
37036             }
37037             this.node.renderIndent(true);
37038         }
37039     },
37040
37041     addClass : function(cls){
37042         if(this.elNode){
37043             Roo.fly(this.elNode).addClass(cls);
37044         }
37045     },
37046
37047     removeClass : function(cls){
37048         if(this.elNode){
37049             Roo.fly(this.elNode).removeClass(cls);
37050         }
37051     },
37052
37053     remove : function(){
37054         if(this.rendered){
37055             this.holder = document.createElement("div");
37056             this.holder.appendChild(this.wrap);
37057         }
37058     },
37059
37060     fireEvent : function(){
37061         return this.node.fireEvent.apply(this.node, arguments);
37062     },
37063
37064     initEvents : function(){
37065         this.node.on("move", this.onMove, this);
37066         var E = Roo.EventManager;
37067         var a = this.anchor;
37068
37069         var el = Roo.fly(a, '_treeui');
37070
37071         if(Roo.isOpera){ // opera render bug ignores the CSS
37072             el.setStyle("text-decoration", "none");
37073         }
37074
37075         el.on("click", this.onClick, this);
37076         el.on("dblclick", this.onDblClick, this);
37077
37078         if(this.checkbox){
37079             Roo.EventManager.on(this.checkbox,
37080                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37081         }
37082
37083         el.on("contextmenu", this.onContextMenu, this);
37084
37085         var icon = Roo.fly(this.iconNode);
37086         icon.on("click", this.onClick, this);
37087         icon.on("dblclick", this.onDblClick, this);
37088         icon.on("contextmenu", this.onContextMenu, this);
37089         E.on(this.ecNode, "click", this.ecClick, this, true);
37090
37091         if(this.node.disabled){
37092             this.addClass("x-tree-node-disabled");
37093         }
37094         if(this.node.hidden){
37095             this.addClass("x-tree-node-disabled");
37096         }
37097         var ot = this.node.getOwnerTree();
37098         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37099         if(dd && (!this.node.isRoot || ot.rootVisible)){
37100             Roo.dd.Registry.register(this.elNode, {
37101                 node: this.node,
37102                 handles: this.getDDHandles(),
37103                 isHandle: false
37104             });
37105         }
37106     },
37107
37108     getDDHandles : function(){
37109         return [this.iconNode, this.textNode];
37110     },
37111
37112     hide : function(){
37113         if(this.rendered){
37114             this.wrap.style.display = "none";
37115         }
37116     },
37117
37118     show : function(){
37119         if(this.rendered){
37120             this.wrap.style.display = "";
37121         }
37122     },
37123
37124     onContextMenu : function(e){
37125         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37126             e.preventDefault();
37127             this.focus();
37128             this.fireEvent("contextmenu", this.node, e);
37129         }
37130     },
37131
37132     onClick : function(e){
37133         if(this.dropping){
37134             e.stopEvent();
37135             return;
37136         }
37137         if(this.fireEvent("beforeclick", this.node, e) !== false){
37138             if(!this.disabled && this.node.attributes.href){
37139                 this.fireEvent("click", this.node, e);
37140                 return;
37141             }
37142             e.preventDefault();
37143             if(this.disabled){
37144                 return;
37145             }
37146
37147             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37148                 this.node.toggle();
37149             }
37150
37151             this.fireEvent("click", this.node, e);
37152         }else{
37153             e.stopEvent();
37154         }
37155     },
37156
37157     onDblClick : function(e){
37158         e.preventDefault();
37159         if(this.disabled){
37160             return;
37161         }
37162         if(this.checkbox){
37163             this.toggleCheck();
37164         }
37165         if(!this.animating && this.node.hasChildNodes()){
37166             this.node.toggle();
37167         }
37168         this.fireEvent("dblclick", this.node, e);
37169     },
37170
37171     onCheckChange : function(){
37172         var checked = this.checkbox.checked;
37173         this.node.attributes.checked = checked;
37174         this.fireEvent('checkchange', this.node, checked);
37175     },
37176
37177     ecClick : function(e){
37178         if(!this.animating && this.node.hasChildNodes()){
37179             this.node.toggle();
37180         }
37181     },
37182
37183     startDrop : function(){
37184         this.dropping = true;
37185     },
37186
37187     // delayed drop so the click event doesn't get fired on a drop
37188     endDrop : function(){
37189        setTimeout(function(){
37190            this.dropping = false;
37191        }.createDelegate(this), 50);
37192     },
37193
37194     expand : function(){
37195         this.updateExpandIcon();
37196         this.ctNode.style.display = "";
37197     },
37198
37199     focus : function(){
37200         if(!this.node.preventHScroll){
37201             try{this.anchor.focus();
37202             }catch(e){}
37203         }else if(!Roo.isIE){
37204             try{
37205                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37206                 var l = noscroll.scrollLeft;
37207                 this.anchor.focus();
37208                 noscroll.scrollLeft = l;
37209             }catch(e){}
37210         }
37211     },
37212
37213     toggleCheck : function(value){
37214         var cb = this.checkbox;
37215         if(cb){
37216             cb.checked = (value === undefined ? !cb.checked : value);
37217         }
37218     },
37219
37220     blur : function(){
37221         try{
37222             this.anchor.blur();
37223         }catch(e){}
37224     },
37225
37226     animExpand : function(callback){
37227         var ct = Roo.get(this.ctNode);
37228         ct.stopFx();
37229         if(!this.node.hasChildNodes()){
37230             this.updateExpandIcon();
37231             this.ctNode.style.display = "";
37232             Roo.callback(callback);
37233             return;
37234         }
37235         this.animating = true;
37236         this.updateExpandIcon();
37237
37238         ct.slideIn('t', {
37239            callback : function(){
37240                this.animating = false;
37241                Roo.callback(callback);
37242             },
37243             scope: this,
37244             duration: this.node.ownerTree.duration || .25
37245         });
37246     },
37247
37248     highlight : function(){
37249         var tree = this.node.getOwnerTree();
37250         Roo.fly(this.wrap).highlight(
37251             tree.hlColor || "C3DAF9",
37252             {endColor: tree.hlBaseColor}
37253         );
37254     },
37255
37256     collapse : function(){
37257         this.updateExpandIcon();
37258         this.ctNode.style.display = "none";
37259     },
37260
37261     animCollapse : function(callback){
37262         var ct = Roo.get(this.ctNode);
37263         ct.enableDisplayMode('block');
37264         ct.stopFx();
37265
37266         this.animating = true;
37267         this.updateExpandIcon();
37268
37269         ct.slideOut('t', {
37270             callback : function(){
37271                this.animating = false;
37272                Roo.callback(callback);
37273             },
37274             scope: this,
37275             duration: this.node.ownerTree.duration || .25
37276         });
37277     },
37278
37279     getContainer : function(){
37280         return this.ctNode;
37281     },
37282
37283     getEl : function(){
37284         return this.wrap;
37285     },
37286
37287     appendDDGhost : function(ghostNode){
37288         ghostNode.appendChild(this.elNode.cloneNode(true));
37289     },
37290
37291     getDDRepairXY : function(){
37292         return Roo.lib.Dom.getXY(this.iconNode);
37293     },
37294
37295     onRender : function(){
37296         this.render();
37297     },
37298
37299     render : function(bulkRender){
37300         var n = this.node, a = n.attributes;
37301         var targetNode = n.parentNode ?
37302               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37303
37304         if(!this.rendered){
37305             this.rendered = true;
37306
37307             this.renderElements(n, a, targetNode, bulkRender);
37308
37309             if(a.qtip){
37310                if(this.textNode.setAttributeNS){
37311                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37312                    if(a.qtipTitle){
37313                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37314                    }
37315                }else{
37316                    this.textNode.setAttribute("ext:qtip", a.qtip);
37317                    if(a.qtipTitle){
37318                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37319                    }
37320                }
37321             }else if(a.qtipCfg){
37322                 a.qtipCfg.target = Roo.id(this.textNode);
37323                 Roo.QuickTips.register(a.qtipCfg);
37324             }
37325             this.initEvents();
37326             if(!this.node.expanded){
37327                 this.updateExpandIcon();
37328             }
37329         }else{
37330             if(bulkRender === true) {
37331                 targetNode.appendChild(this.wrap);
37332             }
37333         }
37334     },
37335
37336     renderElements : function(n, a, targetNode, bulkRender)
37337     {
37338         // add some indent caching, this helps performance when rendering a large tree
37339         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37340         var t = n.getOwnerTree();
37341         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37342         if (typeof(n.attributes.html) != 'undefined') {
37343             txt = n.attributes.html;
37344         }
37345         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37346         var cb = typeof a.checked == 'boolean';
37347         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37348         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37349             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37350             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37351             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37352             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37353             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37354              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37355                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37356             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37357             "</li>"];
37358
37359         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37360             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37361                                 n.nextSibling.ui.getEl(), buf.join(""));
37362         }else{
37363             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37364         }
37365
37366         this.elNode = this.wrap.childNodes[0];
37367         this.ctNode = this.wrap.childNodes[1];
37368         var cs = this.elNode.childNodes;
37369         this.indentNode = cs[0];
37370         this.ecNode = cs[1];
37371         this.iconNode = cs[2];
37372         var index = 3;
37373         if(cb){
37374             this.checkbox = cs[3];
37375             index++;
37376         }
37377         this.anchor = cs[index];
37378         this.textNode = cs[index].firstChild;
37379     },
37380
37381     getAnchor : function(){
37382         return this.anchor;
37383     },
37384
37385     getTextEl : function(){
37386         return this.textNode;
37387     },
37388
37389     getIconEl : function(){
37390         return this.iconNode;
37391     },
37392
37393     isChecked : function(){
37394         return this.checkbox ? this.checkbox.checked : false;
37395     },
37396
37397     updateExpandIcon : function(){
37398         if(this.rendered){
37399             var n = this.node, c1, c2;
37400             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37401             var hasChild = n.hasChildNodes();
37402             if(hasChild){
37403                 if(n.expanded){
37404                     cls += "-minus";
37405                     c1 = "x-tree-node-collapsed";
37406                     c2 = "x-tree-node-expanded";
37407                 }else{
37408                     cls += "-plus";
37409                     c1 = "x-tree-node-expanded";
37410                     c2 = "x-tree-node-collapsed";
37411                 }
37412                 if(this.wasLeaf){
37413                     this.removeClass("x-tree-node-leaf");
37414                     this.wasLeaf = false;
37415                 }
37416                 if(this.c1 != c1 || this.c2 != c2){
37417                     Roo.fly(this.elNode).replaceClass(c1, c2);
37418                     this.c1 = c1; this.c2 = c2;
37419                 }
37420             }else{
37421                 // this changes non-leafs into leafs if they have no children.
37422                 // it's not very rational behaviour..
37423                 
37424                 if(!this.wasLeaf && this.node.leaf){
37425                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37426                     delete this.c1;
37427                     delete this.c2;
37428                     this.wasLeaf = true;
37429                 }
37430             }
37431             var ecc = "x-tree-ec-icon "+cls;
37432             if(this.ecc != ecc){
37433                 this.ecNode.className = ecc;
37434                 this.ecc = ecc;
37435             }
37436         }
37437     },
37438
37439     getChildIndent : function(){
37440         if(!this.childIndent){
37441             var buf = [];
37442             var p = this.node;
37443             while(p){
37444                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37445                     if(!p.isLast()) {
37446                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37447                     } else {
37448                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37449                     }
37450                 }
37451                 p = p.parentNode;
37452             }
37453             this.childIndent = buf.join("");
37454         }
37455         return this.childIndent;
37456     },
37457
37458     renderIndent : function(){
37459         if(this.rendered){
37460             var indent = "";
37461             var p = this.node.parentNode;
37462             if(p){
37463                 indent = p.ui.getChildIndent();
37464             }
37465             if(this.indentMarkup != indent){ // don't rerender if not required
37466                 this.indentNode.innerHTML = indent;
37467                 this.indentMarkup = indent;
37468             }
37469             this.updateExpandIcon();
37470         }
37471     }
37472 };
37473
37474 Roo.tree.RootTreeNodeUI = function(){
37475     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37476 };
37477 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37478     render : function(){
37479         if(!this.rendered){
37480             var targetNode = this.node.ownerTree.innerCt.dom;
37481             this.node.expanded = true;
37482             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37483             this.wrap = this.ctNode = targetNode.firstChild;
37484         }
37485     },
37486     collapse : function(){
37487     },
37488     expand : function(){
37489     }
37490 });/*
37491  * Based on:
37492  * Ext JS Library 1.1.1
37493  * Copyright(c) 2006-2007, Ext JS, LLC.
37494  *
37495  * Originally Released Under LGPL - original licence link has changed is not relivant.
37496  *
37497  * Fork - LGPL
37498  * <script type="text/javascript">
37499  */
37500 /**
37501  * @class Roo.tree.TreeLoader
37502  * @extends Roo.util.Observable
37503  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37504  * nodes from a specified URL. The response must be a javascript Array definition
37505  * who's elements are node definition objects. eg:
37506  * <pre><code>
37507 {  success : true,
37508    data :      [
37509    
37510     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37511     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37512     ]
37513 }
37514
37515
37516 </code></pre>
37517  * <br><br>
37518  * The old style respose with just an array is still supported, but not recommended.
37519  * <br><br>
37520  *
37521  * A server request is sent, and child nodes are loaded only when a node is expanded.
37522  * The loading node's id is passed to the server under the parameter name "node" to
37523  * enable the server to produce the correct child nodes.
37524  * <br><br>
37525  * To pass extra parameters, an event handler may be attached to the "beforeload"
37526  * event, and the parameters specified in the TreeLoader's baseParams property:
37527  * <pre><code>
37528     myTreeLoader.on("beforeload", function(treeLoader, node) {
37529         this.baseParams.category = node.attributes.category;
37530     }, this);
37531     
37532 </code></pre>
37533  *
37534  * This would pass an HTTP parameter called "category" to the server containing
37535  * the value of the Node's "category" attribute.
37536  * @constructor
37537  * Creates a new Treeloader.
37538  * @param {Object} config A config object containing config properties.
37539  */
37540 Roo.tree.TreeLoader = function(config){
37541     this.baseParams = {};
37542     this.requestMethod = "POST";
37543     Roo.apply(this, config);
37544
37545     this.addEvents({
37546     
37547         /**
37548          * @event beforeload
37549          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37550          * @param {Object} This TreeLoader object.
37551          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37552          * @param {Object} callback The callback function specified in the {@link #load} call.
37553          */
37554         beforeload : true,
37555         /**
37556          * @event load
37557          * Fires when the node has been successfuly loaded.
37558          * @param {Object} This TreeLoader object.
37559          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37560          * @param {Object} response The response object containing the data from the server.
37561          */
37562         load : true,
37563         /**
37564          * @event loadexception
37565          * Fires if the network request failed.
37566          * @param {Object} This TreeLoader object.
37567          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37568          * @param {Object} response The response object containing the data from the server.
37569          */
37570         loadexception : true,
37571         /**
37572          * @event create
37573          * Fires before a node is created, enabling you to return custom Node types 
37574          * @param {Object} This TreeLoader object.
37575          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37576          */
37577         create : true
37578     });
37579
37580     Roo.tree.TreeLoader.superclass.constructor.call(this);
37581 };
37582
37583 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37584     /**
37585     * @cfg {String} dataUrl The URL from which to request a Json string which
37586     * specifies an array of node definition object representing the child nodes
37587     * to be loaded.
37588     */
37589     /**
37590     * @cfg {String} requestMethod either GET or POST
37591     * defaults to POST (due to BC)
37592     * to be loaded.
37593     */
37594     /**
37595     * @cfg {Object} baseParams (optional) An object containing properties which
37596     * specify HTTP parameters to be passed to each request for child nodes.
37597     */
37598     /**
37599     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37600     * created by this loader. If the attributes sent by the server have an attribute in this object,
37601     * they take priority.
37602     */
37603     /**
37604     * @cfg {Object} uiProviders (optional) An object containing properties which
37605     * 
37606     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37607     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37608     * <i>uiProvider</i> attribute of a returned child node is a string rather
37609     * than a reference to a TreeNodeUI implementation, this that string value
37610     * is used as a property name in the uiProviders object. You can define the provider named
37611     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37612     */
37613     uiProviders : {},
37614
37615     /**
37616     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37617     * child nodes before loading.
37618     */
37619     clearOnLoad : true,
37620
37621     /**
37622     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37623     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37624     * Grid query { data : [ .....] }
37625     */
37626     
37627     root : false,
37628      /**
37629     * @cfg {String} queryParam (optional) 
37630     * Name of the query as it will be passed on the querystring (defaults to 'node')
37631     * eg. the request will be ?node=[id]
37632     */
37633     
37634     
37635     queryParam: false,
37636     
37637     /**
37638      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37639      * This is called automatically when a node is expanded, but may be used to reload
37640      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37641      * @param {Roo.tree.TreeNode} node
37642      * @param {Function} callback
37643      */
37644     load : function(node, callback){
37645         if(this.clearOnLoad){
37646             while(node.firstChild){
37647                 node.removeChild(node.firstChild);
37648             }
37649         }
37650         if(node.attributes.children){ // preloaded json children
37651             var cs = node.attributes.children;
37652             for(var i = 0, len = cs.length; i < len; i++){
37653                 node.appendChild(this.createNode(cs[i]));
37654             }
37655             if(typeof callback == "function"){
37656                 callback();
37657             }
37658         }else if(this.dataUrl){
37659             this.requestData(node, callback);
37660         }
37661     },
37662
37663     getParams: function(node){
37664         var buf = [], bp = this.baseParams;
37665         for(var key in bp){
37666             if(typeof bp[key] != "function"){
37667                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37668             }
37669         }
37670         var n = this.queryParam === false ? 'node' : this.queryParam;
37671         buf.push(n + "=", encodeURIComponent(node.id));
37672         return buf.join("");
37673     },
37674
37675     requestData : function(node, callback){
37676         if(this.fireEvent("beforeload", this, node, callback) !== false){
37677             this.transId = Roo.Ajax.request({
37678                 method:this.requestMethod,
37679                 url: this.dataUrl||this.url,
37680                 success: this.handleResponse,
37681                 failure: this.handleFailure,
37682                 scope: this,
37683                 argument: {callback: callback, node: node},
37684                 params: this.getParams(node)
37685             });
37686         }else{
37687             // if the load is cancelled, make sure we notify
37688             // the node that we are done
37689             if(typeof callback == "function"){
37690                 callback();
37691             }
37692         }
37693     },
37694
37695     isLoading : function(){
37696         return this.transId ? true : false;
37697     },
37698
37699     abort : function(){
37700         if(this.isLoading()){
37701             Roo.Ajax.abort(this.transId);
37702         }
37703     },
37704
37705     // private
37706     createNode : function(attr)
37707     {
37708         // apply baseAttrs, nice idea Corey!
37709         if(this.baseAttrs){
37710             Roo.applyIf(attr, this.baseAttrs);
37711         }
37712         if(this.applyLoader !== false){
37713             attr.loader = this;
37714         }
37715         // uiProvider = depreciated..
37716         
37717         if(typeof(attr.uiProvider) == 'string'){
37718            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37719                 /**  eval:var:attr */ eval(attr.uiProvider);
37720         }
37721         if(typeof(this.uiProviders['default']) != 'undefined') {
37722             attr.uiProvider = this.uiProviders['default'];
37723         }
37724         
37725         this.fireEvent('create', this, attr);
37726         
37727         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37728         return(attr.leaf ?
37729                         new Roo.tree.TreeNode(attr) :
37730                         new Roo.tree.AsyncTreeNode(attr));
37731     },
37732
37733     processResponse : function(response, node, callback)
37734     {
37735         var json = response.responseText;
37736         try {
37737             
37738             var o = Roo.decode(json);
37739             
37740             if (this.root === false && typeof(o.success) != undefined) {
37741                 this.root = 'data'; // the default behaviour for list like data..
37742                 }
37743                 
37744             if (this.root !== false &&  !o.success) {
37745                 // it's a failure condition.
37746                 var a = response.argument;
37747                 this.fireEvent("loadexception", this, a.node, response);
37748                 Roo.log("Load failed - should have a handler really");
37749                 return;
37750             }
37751             
37752             
37753             
37754             if (this.root !== false) {
37755                  o = o[this.root];
37756             }
37757             
37758             for(var i = 0, len = o.length; i < len; i++){
37759                 var n = this.createNode(o[i]);
37760                 if(n){
37761                     node.appendChild(n);
37762                 }
37763             }
37764             if(typeof callback == "function"){
37765                 callback(this, node);
37766             }
37767         }catch(e){
37768             this.handleFailure(response);
37769         }
37770     },
37771
37772     handleResponse : function(response){
37773         this.transId = false;
37774         var a = response.argument;
37775         this.processResponse(response, a.node, a.callback);
37776         this.fireEvent("load", this, a.node, response);
37777     },
37778
37779     handleFailure : function(response)
37780     {
37781         // should handle failure better..
37782         this.transId = false;
37783         var a = response.argument;
37784         this.fireEvent("loadexception", this, a.node, response);
37785         if(typeof a.callback == "function"){
37786             a.callback(this, a.node);
37787         }
37788     }
37789 });/*
37790  * Based on:
37791  * Ext JS Library 1.1.1
37792  * Copyright(c) 2006-2007, Ext JS, LLC.
37793  *
37794  * Originally Released Under LGPL - original licence link has changed is not relivant.
37795  *
37796  * Fork - LGPL
37797  * <script type="text/javascript">
37798  */
37799
37800 /**
37801 * @class Roo.tree.TreeFilter
37802 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37803 * @param {TreePanel} tree
37804 * @param {Object} config (optional)
37805  */
37806 Roo.tree.TreeFilter = function(tree, config){
37807     this.tree = tree;
37808     this.filtered = {};
37809     Roo.apply(this, config);
37810 };
37811
37812 Roo.tree.TreeFilter.prototype = {
37813     clearBlank:false,
37814     reverse:false,
37815     autoClear:false,
37816     remove:false,
37817
37818      /**
37819      * Filter the data by a specific attribute.
37820      * @param {String/RegExp} value Either string that the attribute value
37821      * should start with or a RegExp to test against the attribute
37822      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37823      * @param {TreeNode} startNode (optional) The node to start the filter at.
37824      */
37825     filter : function(value, attr, startNode){
37826         attr = attr || "text";
37827         var f;
37828         if(typeof value == "string"){
37829             var vlen = value.length;
37830             // auto clear empty filter
37831             if(vlen == 0 && this.clearBlank){
37832                 this.clear();
37833                 return;
37834             }
37835             value = value.toLowerCase();
37836             f = function(n){
37837                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37838             };
37839         }else if(value.exec){ // regex?
37840             f = function(n){
37841                 return value.test(n.attributes[attr]);
37842             };
37843         }else{
37844             throw 'Illegal filter type, must be string or regex';
37845         }
37846         this.filterBy(f, null, startNode);
37847         },
37848
37849     /**
37850      * Filter by a function. The passed function will be called with each
37851      * node in the tree (or from the startNode). If the function returns true, the node is kept
37852      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37853      * @param {Function} fn The filter function
37854      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37855      */
37856     filterBy : function(fn, scope, startNode){
37857         startNode = startNode || this.tree.root;
37858         if(this.autoClear){
37859             this.clear();
37860         }
37861         var af = this.filtered, rv = this.reverse;
37862         var f = function(n){
37863             if(n == startNode){
37864                 return true;
37865             }
37866             if(af[n.id]){
37867                 return false;
37868             }
37869             var m = fn.call(scope || n, n);
37870             if(!m || rv){
37871                 af[n.id] = n;
37872                 n.ui.hide();
37873                 return false;
37874             }
37875             return true;
37876         };
37877         startNode.cascade(f);
37878         if(this.remove){
37879            for(var id in af){
37880                if(typeof id != "function"){
37881                    var n = af[id];
37882                    if(n && n.parentNode){
37883                        n.parentNode.removeChild(n);
37884                    }
37885                }
37886            }
37887         }
37888     },
37889
37890     /**
37891      * Clears the current filter. Note: with the "remove" option
37892      * set a filter cannot be cleared.
37893      */
37894     clear : function(){
37895         var t = this.tree;
37896         var af = this.filtered;
37897         for(var id in af){
37898             if(typeof id != "function"){
37899                 var n = af[id];
37900                 if(n){
37901                     n.ui.show();
37902                 }
37903             }
37904         }
37905         this.filtered = {};
37906     }
37907 };
37908 /*
37909  * Based on:
37910  * Ext JS Library 1.1.1
37911  * Copyright(c) 2006-2007, Ext JS, LLC.
37912  *
37913  * Originally Released Under LGPL - original licence link has changed is not relivant.
37914  *
37915  * Fork - LGPL
37916  * <script type="text/javascript">
37917  */
37918  
37919
37920 /**
37921  * @class Roo.tree.TreeSorter
37922  * Provides sorting of nodes in a TreePanel
37923  * 
37924  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37925  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37926  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37927  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37928  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37929  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37930  * @constructor
37931  * @param {TreePanel} tree
37932  * @param {Object} config
37933  */
37934 Roo.tree.TreeSorter = function(tree, config){
37935     Roo.apply(this, config);
37936     tree.on("beforechildrenrendered", this.doSort, this);
37937     tree.on("append", this.updateSort, this);
37938     tree.on("insert", this.updateSort, this);
37939     
37940     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37941     var p = this.property || "text";
37942     var sortType = this.sortType;
37943     var fs = this.folderSort;
37944     var cs = this.caseSensitive === true;
37945     var leafAttr = this.leafAttr || 'leaf';
37946
37947     this.sortFn = function(n1, n2){
37948         if(fs){
37949             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37950                 return 1;
37951             }
37952             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37953                 return -1;
37954             }
37955         }
37956         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37957         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37958         if(v1 < v2){
37959                         return dsc ? +1 : -1;
37960                 }else if(v1 > v2){
37961                         return dsc ? -1 : +1;
37962         }else{
37963                 return 0;
37964         }
37965     };
37966 };
37967
37968 Roo.tree.TreeSorter.prototype = {
37969     doSort : function(node){
37970         node.sort(this.sortFn);
37971     },
37972     
37973     compareNodes : function(n1, n2){
37974         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37975     },
37976     
37977     updateSort : function(tree, node){
37978         if(node.childrenRendered){
37979             this.doSort.defer(1, this, [node]);
37980         }
37981     }
37982 };/*
37983  * Based on:
37984  * Ext JS Library 1.1.1
37985  * Copyright(c) 2006-2007, Ext JS, LLC.
37986  *
37987  * Originally Released Under LGPL - original licence link has changed is not relivant.
37988  *
37989  * Fork - LGPL
37990  * <script type="text/javascript">
37991  */
37992
37993 if(Roo.dd.DropZone){
37994     
37995 Roo.tree.TreeDropZone = function(tree, config){
37996     this.allowParentInsert = false;
37997     this.allowContainerDrop = false;
37998     this.appendOnly = false;
37999     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38000     this.tree = tree;
38001     this.lastInsertClass = "x-tree-no-status";
38002     this.dragOverData = {};
38003 };
38004
38005 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38006     ddGroup : "TreeDD",
38007     scroll:  true,
38008     
38009     expandDelay : 1000,
38010     
38011     expandNode : function(node){
38012         if(node.hasChildNodes() && !node.isExpanded()){
38013             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38014         }
38015     },
38016     
38017     queueExpand : function(node){
38018         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38019     },
38020     
38021     cancelExpand : function(){
38022         if(this.expandProcId){
38023             clearTimeout(this.expandProcId);
38024             this.expandProcId = false;
38025         }
38026     },
38027     
38028     isValidDropPoint : function(n, pt, dd, e, data){
38029         if(!n || !data){ return false; }
38030         var targetNode = n.node;
38031         var dropNode = data.node;
38032         // default drop rules
38033         if(!(targetNode && targetNode.isTarget && pt)){
38034             return false;
38035         }
38036         if(pt == "append" && targetNode.allowChildren === false){
38037             return false;
38038         }
38039         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38040             return false;
38041         }
38042         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38043             return false;
38044         }
38045         // reuse the object
38046         var overEvent = this.dragOverData;
38047         overEvent.tree = this.tree;
38048         overEvent.target = targetNode;
38049         overEvent.data = data;
38050         overEvent.point = pt;
38051         overEvent.source = dd;
38052         overEvent.rawEvent = e;
38053         overEvent.dropNode = dropNode;
38054         overEvent.cancel = false;  
38055         var result = this.tree.fireEvent("nodedragover", overEvent);
38056         return overEvent.cancel === false && result !== false;
38057     },
38058     
38059     getDropPoint : function(e, n, dd)
38060     {
38061         var tn = n.node;
38062         if(tn.isRoot){
38063             return tn.allowChildren !== false ? "append" : false; // always append for root
38064         }
38065         var dragEl = n.ddel;
38066         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38067         var y = Roo.lib.Event.getPageY(e);
38068         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38069         
38070         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38071         var noAppend = tn.allowChildren === false;
38072         if(this.appendOnly || tn.parentNode.allowChildren === false){
38073             return noAppend ? false : "append";
38074         }
38075         var noBelow = false;
38076         if(!this.allowParentInsert){
38077             noBelow = tn.hasChildNodes() && tn.isExpanded();
38078         }
38079         var q = (b - t) / (noAppend ? 2 : 3);
38080         if(y >= t && y < (t + q)){
38081             return "above";
38082         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38083             return "below";
38084         }else{
38085             return "append";
38086         }
38087     },
38088     
38089     onNodeEnter : function(n, dd, e, data)
38090     {
38091         this.cancelExpand();
38092     },
38093     
38094     onNodeOver : function(n, dd, e, data)
38095     {
38096        
38097         var pt = this.getDropPoint(e, n, dd);
38098         var node = n.node;
38099         
38100         // auto node expand check
38101         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38102             this.queueExpand(node);
38103         }else if(pt != "append"){
38104             this.cancelExpand();
38105         }
38106         
38107         // set the insert point style on the target node
38108         var returnCls = this.dropNotAllowed;
38109         if(this.isValidDropPoint(n, pt, dd, e, data)){
38110            if(pt){
38111                var el = n.ddel;
38112                var cls;
38113                if(pt == "above"){
38114                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38115                    cls = "x-tree-drag-insert-above";
38116                }else if(pt == "below"){
38117                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38118                    cls = "x-tree-drag-insert-below";
38119                }else{
38120                    returnCls = "x-tree-drop-ok-append";
38121                    cls = "x-tree-drag-append";
38122                }
38123                if(this.lastInsertClass != cls){
38124                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38125                    this.lastInsertClass = cls;
38126                }
38127            }
38128        }
38129        return returnCls;
38130     },
38131     
38132     onNodeOut : function(n, dd, e, data){
38133         
38134         this.cancelExpand();
38135         this.removeDropIndicators(n);
38136     },
38137     
38138     onNodeDrop : function(n, dd, e, data){
38139         var point = this.getDropPoint(e, n, dd);
38140         var targetNode = n.node;
38141         targetNode.ui.startDrop();
38142         if(!this.isValidDropPoint(n, point, dd, e, data)){
38143             targetNode.ui.endDrop();
38144             return false;
38145         }
38146         // first try to find the drop node
38147         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38148         var dropEvent = {
38149             tree : this.tree,
38150             target: targetNode,
38151             data: data,
38152             point: point,
38153             source: dd,
38154             rawEvent: e,
38155             dropNode: dropNode,
38156             cancel: !dropNode   
38157         };
38158         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38159         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38160             targetNode.ui.endDrop();
38161             return false;
38162         }
38163         // allow target changing
38164         targetNode = dropEvent.target;
38165         if(point == "append" && !targetNode.isExpanded()){
38166             targetNode.expand(false, null, function(){
38167                 this.completeDrop(dropEvent);
38168             }.createDelegate(this));
38169         }else{
38170             this.completeDrop(dropEvent);
38171         }
38172         return true;
38173     },
38174     
38175     completeDrop : function(de){
38176         var ns = de.dropNode, p = de.point, t = de.target;
38177         if(!(ns instanceof Array)){
38178             ns = [ns];
38179         }
38180         var n;
38181         for(var i = 0, len = ns.length; i < len; i++){
38182             n = ns[i];
38183             if(p == "above"){
38184                 t.parentNode.insertBefore(n, t);
38185             }else if(p == "below"){
38186                 t.parentNode.insertBefore(n, t.nextSibling);
38187             }else{
38188                 t.appendChild(n);
38189             }
38190         }
38191         n.ui.focus();
38192         if(this.tree.hlDrop){
38193             n.ui.highlight();
38194         }
38195         t.ui.endDrop();
38196         this.tree.fireEvent("nodedrop", de);
38197     },
38198     
38199     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38200         if(this.tree.hlDrop){
38201             dropNode.ui.focus();
38202             dropNode.ui.highlight();
38203         }
38204         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38205     },
38206     
38207     getTree : function(){
38208         return this.tree;
38209     },
38210     
38211     removeDropIndicators : function(n){
38212         if(n && n.ddel){
38213             var el = n.ddel;
38214             Roo.fly(el).removeClass([
38215                     "x-tree-drag-insert-above",
38216                     "x-tree-drag-insert-below",
38217                     "x-tree-drag-append"]);
38218             this.lastInsertClass = "_noclass";
38219         }
38220     },
38221     
38222     beforeDragDrop : function(target, e, id){
38223         this.cancelExpand();
38224         return true;
38225     },
38226     
38227     afterRepair : function(data){
38228         if(data && Roo.enableFx){
38229             data.node.ui.highlight();
38230         }
38231         this.hideProxy();
38232     } 
38233     
38234 });
38235
38236 }
38237 /*
38238  * Based on:
38239  * Ext JS Library 1.1.1
38240  * Copyright(c) 2006-2007, Ext JS, LLC.
38241  *
38242  * Originally Released Under LGPL - original licence link has changed is not relivant.
38243  *
38244  * Fork - LGPL
38245  * <script type="text/javascript">
38246  */
38247  
38248
38249 if(Roo.dd.DragZone){
38250 Roo.tree.TreeDragZone = function(tree, config){
38251     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38252     this.tree = tree;
38253 };
38254
38255 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38256     ddGroup : "TreeDD",
38257    
38258     onBeforeDrag : function(data, e){
38259         var n = data.node;
38260         return n && n.draggable && !n.disabled;
38261     },
38262      
38263     
38264     onInitDrag : function(e){
38265         var data = this.dragData;
38266         this.tree.getSelectionModel().select(data.node);
38267         this.proxy.update("");
38268         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38269         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38270     },
38271     
38272     getRepairXY : function(e, data){
38273         return data.node.ui.getDDRepairXY();
38274     },
38275     
38276     onEndDrag : function(data, e){
38277         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38278         
38279         
38280     },
38281     
38282     onValidDrop : function(dd, e, id){
38283         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38284         this.hideProxy();
38285     },
38286     
38287     beforeInvalidDrop : function(e, id){
38288         // this scrolls the original position back into view
38289         var sm = this.tree.getSelectionModel();
38290         sm.clearSelections();
38291         sm.select(this.dragData.node);
38292     }
38293 });
38294 }/*
38295  * Based on:
38296  * Ext JS Library 1.1.1
38297  * Copyright(c) 2006-2007, Ext JS, LLC.
38298  *
38299  * Originally Released Under LGPL - original licence link has changed is not relivant.
38300  *
38301  * Fork - LGPL
38302  * <script type="text/javascript">
38303  */
38304 /**
38305  * @class Roo.tree.TreeEditor
38306  * @extends Roo.Editor
38307  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38308  * as the editor field.
38309  * @constructor
38310  * @param {Object} config (used to be the tree panel.)
38311  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38312  * 
38313  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38314  * @cfg {Roo.form.TextField} field [required] The field configuration
38315  *
38316  * 
38317  */
38318 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38319     var tree = config;
38320     var field;
38321     if (oldconfig) { // old style..
38322         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38323     } else {
38324         // new style..
38325         tree = config.tree;
38326         config.field = config.field  || {};
38327         config.field.xtype = 'TextField';
38328         field = Roo.factory(config.field, Roo.form);
38329     }
38330     config = config || {};
38331     
38332     
38333     this.addEvents({
38334         /**
38335          * @event beforenodeedit
38336          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38337          * false from the handler of this event.
38338          * @param {Editor} this
38339          * @param {Roo.tree.Node} node 
38340          */
38341         "beforenodeedit" : true
38342     });
38343     
38344     //Roo.log(config);
38345     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38346
38347     this.tree = tree;
38348
38349     tree.on('beforeclick', this.beforeNodeClick, this);
38350     tree.getTreeEl().on('mousedown', this.hide, this);
38351     this.on('complete', this.updateNode, this);
38352     this.on('beforestartedit', this.fitToTree, this);
38353     this.on('startedit', this.bindScroll, this, {delay:10});
38354     this.on('specialkey', this.onSpecialKey, this);
38355 };
38356
38357 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38358     /**
38359      * @cfg {String} alignment
38360      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38361      */
38362     alignment: "l-l",
38363     // inherit
38364     autoSize: false,
38365     /**
38366      * @cfg {Boolean} hideEl
38367      * True to hide the bound element while the editor is displayed (defaults to false)
38368      */
38369     hideEl : false,
38370     /**
38371      * @cfg {String} cls
38372      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38373      */
38374     cls: "x-small-editor x-tree-editor",
38375     /**
38376      * @cfg {Boolean} shim
38377      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38378      */
38379     shim:false,
38380     // inherit
38381     shadow:"frame",
38382     /**
38383      * @cfg {Number} maxWidth
38384      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38385      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38386      * scroll and client offsets into account prior to each edit.
38387      */
38388     maxWidth: 250,
38389
38390     editDelay : 350,
38391
38392     // private
38393     fitToTree : function(ed, el){
38394         var td = this.tree.getTreeEl().dom, nd = el.dom;
38395         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38396             td.scrollLeft = nd.offsetLeft;
38397         }
38398         var w = Math.min(
38399                 this.maxWidth,
38400                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38401         this.setSize(w, '');
38402         
38403         return this.fireEvent('beforenodeedit', this, this.editNode);
38404         
38405     },
38406
38407     // private
38408     triggerEdit : function(node){
38409         this.completeEdit();
38410         this.editNode = node;
38411         this.startEdit(node.ui.textNode, node.text);
38412     },
38413
38414     // private
38415     bindScroll : function(){
38416         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38417     },
38418
38419     // private
38420     beforeNodeClick : function(node, e){
38421         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38422         this.lastClick = new Date();
38423         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38424             e.stopEvent();
38425             this.triggerEdit(node);
38426             return false;
38427         }
38428         return true;
38429     },
38430
38431     // private
38432     updateNode : function(ed, value){
38433         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38434         this.editNode.setText(value);
38435     },
38436
38437     // private
38438     onHide : function(){
38439         Roo.tree.TreeEditor.superclass.onHide.call(this);
38440         if(this.editNode){
38441             this.editNode.ui.focus();
38442         }
38443     },
38444
38445     // private
38446     onSpecialKey : function(field, e){
38447         var k = e.getKey();
38448         if(k == e.ESC){
38449             e.stopEvent();
38450             this.cancelEdit();
38451         }else if(k == e.ENTER && !e.hasModifier()){
38452             e.stopEvent();
38453             this.completeEdit();
38454         }
38455     }
38456 });//<Script type="text/javascript">
38457 /*
38458  * Based on:
38459  * Ext JS Library 1.1.1
38460  * Copyright(c) 2006-2007, Ext JS, LLC.
38461  *
38462  * Originally Released Under LGPL - original licence link has changed is not relivant.
38463  *
38464  * Fork - LGPL
38465  * <script type="text/javascript">
38466  */
38467  
38468 /**
38469  * Not documented??? - probably should be...
38470  */
38471
38472 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38473     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38474     
38475     renderElements : function(n, a, targetNode, bulkRender){
38476         //consel.log("renderElements?");
38477         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38478
38479         var t = n.getOwnerTree();
38480         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38481         
38482         var cols = t.columns;
38483         var bw = t.borderWidth;
38484         var c = cols[0];
38485         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38486          var cb = typeof a.checked == "boolean";
38487         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38488         var colcls = 'x-t-' + tid + '-c0';
38489         var buf = [
38490             '<li class="x-tree-node">',
38491             
38492                 
38493                 '<div class="x-tree-node-el ', a.cls,'">',
38494                     // extran...
38495                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38496                 
38497                 
38498                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38499                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38500                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38501                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38502                            (a.iconCls ? ' '+a.iconCls : ''),
38503                            '" unselectable="on" />',
38504                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38505                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38506                              
38507                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38508                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38509                             '<span unselectable="on" qtip="' + tx + '">',
38510                              tx,
38511                              '</span></a>' ,
38512                     '</div>',
38513                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38514                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38515                  ];
38516         for(var i = 1, len = cols.length; i < len; i++){
38517             c = cols[i];
38518             colcls = 'x-t-' + tid + '-c' +i;
38519             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38520             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38521                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38522                       "</div>");
38523          }
38524          
38525          buf.push(
38526             '</a>',
38527             '<div class="x-clear"></div></div>',
38528             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38529             "</li>");
38530         
38531         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38532             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38533                                 n.nextSibling.ui.getEl(), buf.join(""));
38534         }else{
38535             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38536         }
38537         var el = this.wrap.firstChild;
38538         this.elRow = el;
38539         this.elNode = el.firstChild;
38540         this.ranchor = el.childNodes[1];
38541         this.ctNode = this.wrap.childNodes[1];
38542         var cs = el.firstChild.childNodes;
38543         this.indentNode = cs[0];
38544         this.ecNode = cs[1];
38545         this.iconNode = cs[2];
38546         var index = 3;
38547         if(cb){
38548             this.checkbox = cs[3];
38549             index++;
38550         }
38551         this.anchor = cs[index];
38552         
38553         this.textNode = cs[index].firstChild;
38554         
38555         //el.on("click", this.onClick, this);
38556         //el.on("dblclick", this.onDblClick, this);
38557         
38558         
38559        // console.log(this);
38560     },
38561     initEvents : function(){
38562         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38563         
38564             
38565         var a = this.ranchor;
38566
38567         var el = Roo.get(a);
38568
38569         if(Roo.isOpera){ // opera render bug ignores the CSS
38570             el.setStyle("text-decoration", "none");
38571         }
38572
38573         el.on("click", this.onClick, this);
38574         el.on("dblclick", this.onDblClick, this);
38575         el.on("contextmenu", this.onContextMenu, this);
38576         
38577     },
38578     
38579     /*onSelectedChange : function(state){
38580         if(state){
38581             this.focus();
38582             this.addClass("x-tree-selected");
38583         }else{
38584             //this.blur();
38585             this.removeClass("x-tree-selected");
38586         }
38587     },*/
38588     addClass : function(cls){
38589         if(this.elRow){
38590             Roo.fly(this.elRow).addClass(cls);
38591         }
38592         
38593     },
38594     
38595     
38596     removeClass : function(cls){
38597         if(this.elRow){
38598             Roo.fly(this.elRow).removeClass(cls);
38599         }
38600     }
38601
38602     
38603     
38604 });//<Script type="text/javascript">
38605
38606 /*
38607  * Based on:
38608  * Ext JS Library 1.1.1
38609  * Copyright(c) 2006-2007, Ext JS, LLC.
38610  *
38611  * Originally Released Under LGPL - original licence link has changed is not relivant.
38612  *
38613  * Fork - LGPL
38614  * <script type="text/javascript">
38615  */
38616  
38617
38618 /**
38619  * @class Roo.tree.ColumnTree
38620  * @extends Roo.tree.TreePanel
38621  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38622  * @cfg {int} borderWidth  compined right/left border allowance
38623  * @constructor
38624  * @param {String/HTMLElement/Element} el The container element
38625  * @param {Object} config
38626  */
38627 Roo.tree.ColumnTree =  function(el, config)
38628 {
38629    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38630    this.addEvents({
38631         /**
38632         * @event resize
38633         * Fire this event on a container when it resizes
38634         * @param {int} w Width
38635         * @param {int} h Height
38636         */
38637        "resize" : true
38638     });
38639     this.on('resize', this.onResize, this);
38640 };
38641
38642 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38643     //lines:false,
38644     
38645     
38646     borderWidth: Roo.isBorderBox ? 0 : 2, 
38647     headEls : false,
38648     
38649     render : function(){
38650         // add the header.....
38651        
38652         Roo.tree.ColumnTree.superclass.render.apply(this);
38653         
38654         this.el.addClass('x-column-tree');
38655         
38656         this.headers = this.el.createChild(
38657             {cls:'x-tree-headers'},this.innerCt.dom);
38658    
38659         var cols = this.columns, c;
38660         var totalWidth = 0;
38661         this.headEls = [];
38662         var  len = cols.length;
38663         for(var i = 0; i < len; i++){
38664              c = cols[i];
38665              totalWidth += c.width;
38666             this.headEls.push(this.headers.createChild({
38667                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38668                  cn: {
38669                      cls:'x-tree-hd-text',
38670                      html: c.header
38671                  },
38672                  style:'width:'+(c.width-this.borderWidth)+'px;'
38673              }));
38674         }
38675         this.headers.createChild({cls:'x-clear'});
38676         // prevent floats from wrapping when clipped
38677         this.headers.setWidth(totalWidth);
38678         //this.innerCt.setWidth(totalWidth);
38679         this.innerCt.setStyle({ overflow: 'auto' });
38680         this.onResize(this.width, this.height);
38681              
38682         
38683     },
38684     onResize : function(w,h)
38685     {
38686         this.height = h;
38687         this.width = w;
38688         // resize cols..
38689         this.innerCt.setWidth(this.width);
38690         this.innerCt.setHeight(this.height-20);
38691         
38692         // headers...
38693         var cols = this.columns, c;
38694         var totalWidth = 0;
38695         var expEl = false;
38696         var len = cols.length;
38697         for(var i = 0; i < len; i++){
38698             c = cols[i];
38699             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38700                 // it's the expander..
38701                 expEl  = this.headEls[i];
38702                 continue;
38703             }
38704             totalWidth += c.width;
38705             
38706         }
38707         if (expEl) {
38708             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38709         }
38710         this.headers.setWidth(w-20);
38711
38712         
38713         
38714         
38715     }
38716 });
38717 /*
38718  * Based on:
38719  * Ext JS Library 1.1.1
38720  * Copyright(c) 2006-2007, Ext JS, LLC.
38721  *
38722  * Originally Released Under LGPL - original licence link has changed is not relivant.
38723  *
38724  * Fork - LGPL
38725  * <script type="text/javascript">
38726  */
38727  
38728 /**
38729  * @class Roo.menu.Menu
38730  * @extends Roo.util.Observable
38731  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38732  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38733  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38734  * @constructor
38735  * Creates a new Menu
38736  * @param {Object} config Configuration options
38737  */
38738 Roo.menu.Menu = function(config){
38739     
38740     Roo.menu.Menu.superclass.constructor.call(this, config);
38741     
38742     this.id = this.id || Roo.id();
38743     this.addEvents({
38744         /**
38745          * @event beforeshow
38746          * Fires before this menu is displayed
38747          * @param {Roo.menu.Menu} this
38748          */
38749         beforeshow : true,
38750         /**
38751          * @event beforehide
38752          * Fires before this menu is hidden
38753          * @param {Roo.menu.Menu} this
38754          */
38755         beforehide : true,
38756         /**
38757          * @event show
38758          * Fires after this menu is displayed
38759          * @param {Roo.menu.Menu} this
38760          */
38761         show : true,
38762         /**
38763          * @event hide
38764          * Fires after this menu is hidden
38765          * @param {Roo.menu.Menu} this
38766          */
38767         hide : true,
38768         /**
38769          * @event click
38770          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38771          * @param {Roo.menu.Menu} this
38772          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38773          * @param {Roo.EventObject} e
38774          */
38775         click : true,
38776         /**
38777          * @event mouseover
38778          * Fires when the mouse is hovering over this menu
38779          * @param {Roo.menu.Menu} this
38780          * @param {Roo.EventObject} e
38781          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38782          */
38783         mouseover : true,
38784         /**
38785          * @event mouseout
38786          * Fires when the mouse exits this menu
38787          * @param {Roo.menu.Menu} this
38788          * @param {Roo.EventObject} e
38789          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38790          */
38791         mouseout : true,
38792         /**
38793          * @event itemclick
38794          * Fires when a menu item contained in this menu is clicked
38795          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38796          * @param {Roo.EventObject} e
38797          */
38798         itemclick: true
38799     });
38800     if (this.registerMenu) {
38801         Roo.menu.MenuMgr.register(this);
38802     }
38803     
38804     var mis = this.items;
38805     this.items = new Roo.util.MixedCollection();
38806     if(mis){
38807         this.add.apply(this, mis);
38808     }
38809 };
38810
38811 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38812     /**
38813      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38814      */
38815     minWidth : 120,
38816     /**
38817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38818      * for bottom-right shadow (defaults to "sides")
38819      */
38820     shadow : "sides",
38821     /**
38822      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38823      * this menu (defaults to "tl-tr?")
38824      */
38825     subMenuAlign : "tl-tr?",
38826     /**
38827      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38828      * relative to its element of origin (defaults to "tl-bl?")
38829      */
38830     defaultAlign : "tl-bl?",
38831     /**
38832      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38833      */
38834     allowOtherMenus : false,
38835     /**
38836      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38837      */
38838     registerMenu : true,
38839
38840     hidden:true,
38841
38842     // private
38843     render : function(){
38844         if(this.el){
38845             return;
38846         }
38847         var el = this.el = new Roo.Layer({
38848             cls: "x-menu",
38849             shadow:this.shadow,
38850             constrain: false,
38851             parentEl: this.parentEl || document.body,
38852             zindex:15000
38853         });
38854
38855         this.keyNav = new Roo.menu.MenuNav(this);
38856
38857         if(this.plain){
38858             el.addClass("x-menu-plain");
38859         }
38860         if(this.cls){
38861             el.addClass(this.cls);
38862         }
38863         // generic focus element
38864         this.focusEl = el.createChild({
38865             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38866         });
38867         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38868         //disabling touch- as it's causing issues ..
38869         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38870         ul.on('click'   , this.onClick, this);
38871         
38872         
38873         ul.on("mouseover", this.onMouseOver, this);
38874         ul.on("mouseout", this.onMouseOut, this);
38875         this.items.each(function(item){
38876             if (item.hidden) {
38877                 return;
38878             }
38879             
38880             var li = document.createElement("li");
38881             li.className = "x-menu-list-item";
38882             ul.dom.appendChild(li);
38883             item.render(li, this);
38884         }, this);
38885         this.ul = ul;
38886         this.autoWidth();
38887     },
38888
38889     // private
38890     autoWidth : function(){
38891         var el = this.el, ul = this.ul;
38892         if(!el){
38893             return;
38894         }
38895         var w = this.width;
38896         if(w){
38897             el.setWidth(w);
38898         }else if(Roo.isIE){
38899             el.setWidth(this.minWidth);
38900             var t = el.dom.offsetWidth; // force recalc
38901             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38902         }
38903     },
38904
38905     // private
38906     delayAutoWidth : function(){
38907         if(this.rendered){
38908             if(!this.awTask){
38909                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38910             }
38911             this.awTask.delay(20);
38912         }
38913     },
38914
38915     // private
38916     findTargetItem : function(e){
38917         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38918         if(t && t.menuItemId){
38919             return this.items.get(t.menuItemId);
38920         }
38921     },
38922
38923     // private
38924     onClick : function(e){
38925         Roo.log("menu.onClick");
38926         var t = this.findTargetItem(e);
38927         if(!t){
38928             return;
38929         }
38930         Roo.log(e);
38931         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38932             if(t == this.activeItem && t.shouldDeactivate(e)){
38933                 this.activeItem.deactivate();
38934                 delete this.activeItem;
38935                 return;
38936             }
38937             if(t.canActivate){
38938                 this.setActiveItem(t, true);
38939             }
38940             return;
38941             
38942             
38943         }
38944         
38945         t.onClick(e);
38946         this.fireEvent("click", this, t, e);
38947     },
38948
38949     // private
38950     setActiveItem : function(item, autoExpand){
38951         if(item != this.activeItem){
38952             if(this.activeItem){
38953                 this.activeItem.deactivate();
38954             }
38955             this.activeItem = item;
38956             item.activate(autoExpand);
38957         }else if(autoExpand){
38958             item.expandMenu();
38959         }
38960     },
38961
38962     // private
38963     tryActivate : function(start, step){
38964         var items = this.items;
38965         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38966             var item = items.get(i);
38967             if(!item.disabled && item.canActivate){
38968                 this.setActiveItem(item, false);
38969                 return item;
38970             }
38971         }
38972         return false;
38973     },
38974
38975     // private
38976     onMouseOver : function(e){
38977         var t;
38978         if(t = this.findTargetItem(e)){
38979             if(t.canActivate && !t.disabled){
38980                 this.setActiveItem(t, true);
38981             }
38982         }
38983         this.fireEvent("mouseover", this, e, t);
38984     },
38985
38986     // private
38987     onMouseOut : function(e){
38988         var t;
38989         if(t = this.findTargetItem(e)){
38990             if(t == this.activeItem && t.shouldDeactivate(e)){
38991                 this.activeItem.deactivate();
38992                 delete this.activeItem;
38993             }
38994         }
38995         this.fireEvent("mouseout", this, e, t);
38996     },
38997
38998     /**
38999      * Read-only.  Returns true if the menu is currently displayed, else false.
39000      * @type Boolean
39001      */
39002     isVisible : function(){
39003         return this.el && !this.hidden;
39004     },
39005
39006     /**
39007      * Displays this menu relative to another element
39008      * @param {String/HTMLElement/Roo.Element} element The element to align to
39009      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39010      * the element (defaults to this.defaultAlign)
39011      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39012      */
39013     show : function(el, pos, parentMenu){
39014         this.parentMenu = parentMenu;
39015         if(!this.el){
39016             this.render();
39017         }
39018         this.fireEvent("beforeshow", this);
39019         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39020     },
39021
39022     /**
39023      * Displays this menu at a specific xy position
39024      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39025      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39026      */
39027     showAt : function(xy, parentMenu, /* private: */_e){
39028         this.parentMenu = parentMenu;
39029         if(!this.el){
39030             this.render();
39031         }
39032         if(_e !== false){
39033             this.fireEvent("beforeshow", this);
39034             xy = this.el.adjustForConstraints(xy);
39035         }
39036         this.el.setXY(xy);
39037         this.el.show();
39038         this.hidden = false;
39039         this.focus();
39040         this.fireEvent("show", this);
39041     },
39042
39043     focus : function(){
39044         if(!this.hidden){
39045             this.doFocus.defer(50, this);
39046         }
39047     },
39048
39049     doFocus : function(){
39050         if(!this.hidden){
39051             this.focusEl.focus();
39052         }
39053     },
39054
39055     /**
39056      * Hides this menu and optionally all parent menus
39057      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39058      */
39059     hide : function(deep){
39060         if(this.el && this.isVisible()){
39061             this.fireEvent("beforehide", this);
39062             if(this.activeItem){
39063                 this.activeItem.deactivate();
39064                 this.activeItem = null;
39065             }
39066             this.el.hide();
39067             this.hidden = true;
39068             this.fireEvent("hide", this);
39069         }
39070         if(deep === true && this.parentMenu){
39071             this.parentMenu.hide(true);
39072         }
39073     },
39074
39075     /**
39076      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39077      * Any of the following are valid:
39078      * <ul>
39079      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39080      * <li>An HTMLElement object which will be converted to a menu item</li>
39081      * <li>A menu item config object that will be created as a new menu item</li>
39082      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39083      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39084      * </ul>
39085      * Usage:
39086      * <pre><code>
39087 // Create the menu
39088 var menu = new Roo.menu.Menu();
39089
39090 // Create a menu item to add by reference
39091 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39092
39093 // Add a bunch of items at once using different methods.
39094 // Only the last item added will be returned.
39095 var item = menu.add(
39096     menuItem,                // add existing item by ref
39097     'Dynamic Item',          // new TextItem
39098     '-',                     // new separator
39099     { text: 'Config Item' }  // new item by config
39100 );
39101 </code></pre>
39102      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39103      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39104      */
39105     add : function(){
39106         var a = arguments, l = a.length, item;
39107         for(var i = 0; i < l; i++){
39108             var el = a[i];
39109             if ((typeof(el) == "object") && el.xtype && el.xns) {
39110                 el = Roo.factory(el, Roo.menu);
39111             }
39112             
39113             if(el.render){ // some kind of Item
39114                 item = this.addItem(el);
39115             }else if(typeof el == "string"){ // string
39116                 if(el == "separator" || el == "-"){
39117                     item = this.addSeparator();
39118                 }else{
39119                     item = this.addText(el);
39120                 }
39121             }else if(el.tagName || el.el){ // element
39122                 item = this.addElement(el);
39123             }else if(typeof el == "object"){ // must be menu item config?
39124                 item = this.addMenuItem(el);
39125             }
39126         }
39127         return item;
39128     },
39129
39130     /**
39131      * Returns this menu's underlying {@link Roo.Element} object
39132      * @return {Roo.Element} The element
39133      */
39134     getEl : function(){
39135         if(!this.el){
39136             this.render();
39137         }
39138         return this.el;
39139     },
39140
39141     /**
39142      * Adds a separator bar to the menu
39143      * @return {Roo.menu.Item} The menu item that was added
39144      */
39145     addSeparator : function(){
39146         return this.addItem(new Roo.menu.Separator());
39147     },
39148
39149     /**
39150      * Adds an {@link Roo.Element} object to the menu
39151      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39152      * @return {Roo.menu.Item} The menu item that was added
39153      */
39154     addElement : function(el){
39155         return this.addItem(new Roo.menu.BaseItem(el));
39156     },
39157
39158     /**
39159      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39160      * @param {Roo.menu.Item} item The menu item to add
39161      * @return {Roo.menu.Item} The menu item that was added
39162      */
39163     addItem : function(item){
39164         this.items.add(item);
39165         if(this.ul){
39166             var li = document.createElement("li");
39167             li.className = "x-menu-list-item";
39168             this.ul.dom.appendChild(li);
39169             item.render(li, this);
39170             this.delayAutoWidth();
39171         }
39172         return item;
39173     },
39174
39175     /**
39176      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39177      * @param {Object} config A MenuItem config object
39178      * @return {Roo.menu.Item} The menu item that was added
39179      */
39180     addMenuItem : function(config){
39181         if(!(config instanceof Roo.menu.Item)){
39182             if(typeof config.checked == "boolean"){ // must be check menu item config?
39183                 config = new Roo.menu.CheckItem(config);
39184             }else{
39185                 config = new Roo.menu.Item(config);
39186             }
39187         }
39188         return this.addItem(config);
39189     },
39190
39191     /**
39192      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39193      * @param {String} text The text to display in the menu item
39194      * @return {Roo.menu.Item} The menu item that was added
39195      */
39196     addText : function(text){
39197         return this.addItem(new Roo.menu.TextItem({ text : text }));
39198     },
39199
39200     /**
39201      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39202      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39203      * @param {Roo.menu.Item} item The menu item to add
39204      * @return {Roo.menu.Item} The menu item that was added
39205      */
39206     insert : function(index, item){
39207         this.items.insert(index, item);
39208         if(this.ul){
39209             var li = document.createElement("li");
39210             li.className = "x-menu-list-item";
39211             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39212             item.render(li, this);
39213             this.delayAutoWidth();
39214         }
39215         return item;
39216     },
39217
39218     /**
39219      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39220      * @param {Roo.menu.Item} item The menu item to remove
39221      */
39222     remove : function(item){
39223         this.items.removeKey(item.id);
39224         item.destroy();
39225     },
39226
39227     /**
39228      * Removes and destroys all items in the menu
39229      */
39230     removeAll : function(){
39231         var f;
39232         while(f = this.items.first()){
39233             this.remove(f);
39234         }
39235     }
39236 });
39237
39238 // MenuNav is a private utility class used internally by the Menu
39239 Roo.menu.MenuNav = function(menu){
39240     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39241     this.scope = this.menu = menu;
39242 };
39243
39244 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39245     doRelay : function(e, h){
39246         var k = e.getKey();
39247         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39248             this.menu.tryActivate(0, 1);
39249             return false;
39250         }
39251         return h.call(this.scope || this, e, this.menu);
39252     },
39253
39254     up : function(e, m){
39255         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39256             m.tryActivate(m.items.length-1, -1);
39257         }
39258     },
39259
39260     down : function(e, m){
39261         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39262             m.tryActivate(0, 1);
39263         }
39264     },
39265
39266     right : function(e, m){
39267         if(m.activeItem){
39268             m.activeItem.expandMenu(true);
39269         }
39270     },
39271
39272     left : function(e, m){
39273         m.hide();
39274         if(m.parentMenu && m.parentMenu.activeItem){
39275             m.parentMenu.activeItem.activate();
39276         }
39277     },
39278
39279     enter : function(e, m){
39280         if(m.activeItem){
39281             e.stopPropagation();
39282             m.activeItem.onClick(e);
39283             m.fireEvent("click", this, m.activeItem);
39284             return true;
39285         }
39286     }
39287 });/*
39288  * Based on:
39289  * Ext JS Library 1.1.1
39290  * Copyright(c) 2006-2007, Ext JS, LLC.
39291  *
39292  * Originally Released Under LGPL - original licence link has changed is not relivant.
39293  *
39294  * Fork - LGPL
39295  * <script type="text/javascript">
39296  */
39297  
39298 /**
39299  * @class Roo.menu.MenuMgr
39300  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39301  * @static
39302  */
39303 Roo.menu.MenuMgr = function(){
39304    var menus, active, groups = {}, attached = false, lastShow = new Date();
39305
39306    // private - called when first menu is created
39307    function init(){
39308        menus = {};
39309        active = new Roo.util.MixedCollection();
39310        Roo.get(document).addKeyListener(27, function(){
39311            if(active.length > 0){
39312                hideAll();
39313            }
39314        });
39315    }
39316
39317    // private
39318    function hideAll(){
39319        if(active && active.length > 0){
39320            var c = active.clone();
39321            c.each(function(m){
39322                m.hide();
39323            });
39324        }
39325    }
39326
39327    // private
39328    function onHide(m){
39329        active.remove(m);
39330        if(active.length < 1){
39331            Roo.get(document).un("mousedown", onMouseDown);
39332            attached = false;
39333        }
39334    }
39335
39336    // private
39337    function onShow(m){
39338        var last = active.last();
39339        lastShow = new Date();
39340        active.add(m);
39341        if(!attached){
39342            Roo.get(document).on("mousedown", onMouseDown);
39343            attached = true;
39344        }
39345        if(m.parentMenu){
39346           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39347           m.parentMenu.activeChild = m;
39348        }else if(last && last.isVisible()){
39349           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39350        }
39351    }
39352
39353    // private
39354    function onBeforeHide(m){
39355        if(m.activeChild){
39356            m.activeChild.hide();
39357        }
39358        if(m.autoHideTimer){
39359            clearTimeout(m.autoHideTimer);
39360            delete m.autoHideTimer;
39361        }
39362    }
39363
39364    // private
39365    function onBeforeShow(m){
39366        var pm = m.parentMenu;
39367        if(!pm && !m.allowOtherMenus){
39368            hideAll();
39369        }else if(pm && pm.activeChild && active != m){
39370            pm.activeChild.hide();
39371        }
39372    }
39373
39374    // private
39375    function onMouseDown(e){
39376        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39377            hideAll();
39378        }
39379    }
39380
39381    // private
39382    function onBeforeCheck(mi, state){
39383        if(state){
39384            var g = groups[mi.group];
39385            for(var i = 0, l = g.length; i < l; i++){
39386                if(g[i] != mi){
39387                    g[i].setChecked(false);
39388                }
39389            }
39390        }
39391    }
39392
39393    return {
39394
39395        /**
39396         * Hides all menus that are currently visible
39397         */
39398        hideAll : function(){
39399             hideAll();  
39400        },
39401
39402        // private
39403        register : function(menu){
39404            if(!menus){
39405                init();
39406            }
39407            menus[menu.id] = menu;
39408            menu.on("beforehide", onBeforeHide);
39409            menu.on("hide", onHide);
39410            menu.on("beforeshow", onBeforeShow);
39411            menu.on("show", onShow);
39412            var g = menu.group;
39413            if(g && menu.events["checkchange"]){
39414                if(!groups[g]){
39415                    groups[g] = [];
39416                }
39417                groups[g].push(menu);
39418                menu.on("checkchange", onCheck);
39419            }
39420        },
39421
39422         /**
39423          * Returns a {@link Roo.menu.Menu} object
39424          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39425          * be used to generate and return a new Menu instance.
39426          */
39427        get : function(menu){
39428            if(typeof menu == "string"){ // menu id
39429                return menus[menu];
39430            }else if(menu.events){  // menu instance
39431                return menu;
39432            }else if(typeof menu.length == 'number'){ // array of menu items?
39433                return new Roo.menu.Menu({items:menu});
39434            }else{ // otherwise, must be a config
39435                return new Roo.menu.Menu(menu);
39436            }
39437        },
39438
39439        // private
39440        unregister : function(menu){
39441            delete menus[menu.id];
39442            menu.un("beforehide", onBeforeHide);
39443            menu.un("hide", onHide);
39444            menu.un("beforeshow", onBeforeShow);
39445            menu.un("show", onShow);
39446            var g = menu.group;
39447            if(g && menu.events["checkchange"]){
39448                groups[g].remove(menu);
39449                menu.un("checkchange", onCheck);
39450            }
39451        },
39452
39453        // private
39454        registerCheckable : function(menuItem){
39455            var g = menuItem.group;
39456            if(g){
39457                if(!groups[g]){
39458                    groups[g] = [];
39459                }
39460                groups[g].push(menuItem);
39461                menuItem.on("beforecheckchange", onBeforeCheck);
39462            }
39463        },
39464
39465        // private
39466        unregisterCheckable : function(menuItem){
39467            var g = menuItem.group;
39468            if(g){
39469                groups[g].remove(menuItem);
39470                menuItem.un("beforecheckchange", onBeforeCheck);
39471            }
39472        }
39473    };
39474 }();/*
39475  * Based on:
39476  * Ext JS Library 1.1.1
39477  * Copyright(c) 2006-2007, Ext JS, LLC.
39478  *
39479  * Originally Released Under LGPL - original licence link has changed is not relivant.
39480  *
39481  * Fork - LGPL
39482  * <script type="text/javascript">
39483  */
39484  
39485
39486 /**
39487  * @class Roo.menu.BaseItem
39488  * @extends Roo.Component
39489  * @abstract
39490  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39491  * management and base configuration options shared by all menu components.
39492  * @constructor
39493  * Creates a new BaseItem
39494  * @param {Object} config Configuration options
39495  */
39496 Roo.menu.BaseItem = function(config){
39497     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39498
39499     this.addEvents({
39500         /**
39501          * @event click
39502          * Fires when this item is clicked
39503          * @param {Roo.menu.BaseItem} this
39504          * @param {Roo.EventObject} e
39505          */
39506         click: true,
39507         /**
39508          * @event activate
39509          * Fires when this item is activated
39510          * @param {Roo.menu.BaseItem} this
39511          */
39512         activate : true,
39513         /**
39514          * @event deactivate
39515          * Fires when this item is deactivated
39516          * @param {Roo.menu.BaseItem} this
39517          */
39518         deactivate : true
39519     });
39520
39521     if(this.handler){
39522         this.on("click", this.handler, this.scope, true);
39523     }
39524 };
39525
39526 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39527     /**
39528      * @cfg {Function} handler
39529      * A function that will handle the click event of this menu item (defaults to undefined)
39530      */
39531     /**
39532      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39533      */
39534     canActivate : false,
39535     
39536      /**
39537      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39538      */
39539     hidden: false,
39540     
39541     /**
39542      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39543      */
39544     activeClass : "x-menu-item-active",
39545     /**
39546      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39547      */
39548     hideOnClick : true,
39549     /**
39550      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39551      */
39552     hideDelay : 100,
39553
39554     // private
39555     ctype: "Roo.menu.BaseItem",
39556
39557     // private
39558     actionMode : "container",
39559
39560     // private
39561     render : function(container, parentMenu){
39562         this.parentMenu = parentMenu;
39563         Roo.menu.BaseItem.superclass.render.call(this, container);
39564         this.container.menuItemId = this.id;
39565     },
39566
39567     // private
39568     onRender : function(container, position){
39569         this.el = Roo.get(this.el);
39570         container.dom.appendChild(this.el.dom);
39571     },
39572
39573     // private
39574     onClick : function(e){
39575         if(!this.disabled && this.fireEvent("click", this, e) !== false
39576                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39577             this.handleClick(e);
39578         }else{
39579             e.stopEvent();
39580         }
39581     },
39582
39583     // private
39584     activate : function(){
39585         if(this.disabled){
39586             return false;
39587         }
39588         var li = this.container;
39589         li.addClass(this.activeClass);
39590         this.region = li.getRegion().adjust(2, 2, -2, -2);
39591         this.fireEvent("activate", this);
39592         return true;
39593     },
39594
39595     // private
39596     deactivate : function(){
39597         this.container.removeClass(this.activeClass);
39598         this.fireEvent("deactivate", this);
39599     },
39600
39601     // private
39602     shouldDeactivate : function(e){
39603         return !this.region || !this.region.contains(e.getPoint());
39604     },
39605
39606     // private
39607     handleClick : function(e){
39608         if(this.hideOnClick){
39609             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39610         }
39611     },
39612
39613     // private
39614     expandMenu : function(autoActivate){
39615         // do nothing
39616     },
39617
39618     // private
39619     hideMenu : function(){
39620         // do nothing
39621     }
39622 });/*
39623  * Based on:
39624  * Ext JS Library 1.1.1
39625  * Copyright(c) 2006-2007, Ext JS, LLC.
39626  *
39627  * Originally Released Under LGPL - original licence link has changed is not relivant.
39628  *
39629  * Fork - LGPL
39630  * <script type="text/javascript">
39631  */
39632  
39633 /**
39634  * @class Roo.menu.Adapter
39635  * @extends Roo.menu.BaseItem
39636  * @abstract
39637  * 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.
39638  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39639  * @constructor
39640  * Creates a new Adapter
39641  * @param {Object} config Configuration options
39642  */
39643 Roo.menu.Adapter = function(component, config){
39644     Roo.menu.Adapter.superclass.constructor.call(this, config);
39645     this.component = component;
39646 };
39647 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39648     // private
39649     canActivate : true,
39650
39651     // private
39652     onRender : function(container, position){
39653         this.component.render(container);
39654         this.el = this.component.getEl();
39655     },
39656
39657     // private
39658     activate : function(){
39659         if(this.disabled){
39660             return false;
39661         }
39662         this.component.focus();
39663         this.fireEvent("activate", this);
39664         return true;
39665     },
39666
39667     // private
39668     deactivate : function(){
39669         this.fireEvent("deactivate", this);
39670     },
39671
39672     // private
39673     disable : function(){
39674         this.component.disable();
39675         Roo.menu.Adapter.superclass.disable.call(this);
39676     },
39677
39678     // private
39679     enable : function(){
39680         this.component.enable();
39681         Roo.menu.Adapter.superclass.enable.call(this);
39682     }
39683 });/*
39684  * Based on:
39685  * Ext JS Library 1.1.1
39686  * Copyright(c) 2006-2007, Ext JS, LLC.
39687  *
39688  * Originally Released Under LGPL - original licence link has changed is not relivant.
39689  *
39690  * Fork - LGPL
39691  * <script type="text/javascript">
39692  */
39693
39694 /**
39695  * @class Roo.menu.TextItem
39696  * @extends Roo.menu.BaseItem
39697  * Adds a static text string to a menu, usually used as either a heading or group separator.
39698  * Note: old style constructor with text is still supported.
39699  * 
39700  * @constructor
39701  * Creates a new TextItem
39702  * @param {Object} cfg Configuration
39703  */
39704 Roo.menu.TextItem = function(cfg){
39705     if (typeof(cfg) == 'string') {
39706         this.text = cfg;
39707     } else {
39708         Roo.apply(this,cfg);
39709     }
39710     
39711     Roo.menu.TextItem.superclass.constructor.call(this);
39712 };
39713
39714 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39715     /**
39716      * @cfg {String} text Text to show on item.
39717      */
39718     text : '',
39719     
39720     /**
39721      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39722      */
39723     hideOnClick : false,
39724     /**
39725      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39726      */
39727     itemCls : "x-menu-text",
39728
39729     // private
39730     onRender : function(){
39731         var s = document.createElement("span");
39732         s.className = this.itemCls;
39733         s.innerHTML = this.text;
39734         this.el = s;
39735         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39736     }
39737 });/*
39738  * Based on:
39739  * Ext JS Library 1.1.1
39740  * Copyright(c) 2006-2007, Ext JS, LLC.
39741  *
39742  * Originally Released Under LGPL - original licence link has changed is not relivant.
39743  *
39744  * Fork - LGPL
39745  * <script type="text/javascript">
39746  */
39747
39748 /**
39749  * @class Roo.menu.Separator
39750  * @extends Roo.menu.BaseItem
39751  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39752  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39753  * @constructor
39754  * @param {Object} config Configuration options
39755  */
39756 Roo.menu.Separator = function(config){
39757     Roo.menu.Separator.superclass.constructor.call(this, config);
39758 };
39759
39760 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39761     /**
39762      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39763      */
39764     itemCls : "x-menu-sep",
39765     /**
39766      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39767      */
39768     hideOnClick : false,
39769
39770     // private
39771     onRender : function(li){
39772         var s = document.createElement("span");
39773         s.className = this.itemCls;
39774         s.innerHTML = "&#160;";
39775         this.el = s;
39776         li.addClass("x-menu-sep-li");
39777         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39778     }
39779 });/*
39780  * Based on:
39781  * Ext JS Library 1.1.1
39782  * Copyright(c) 2006-2007, Ext JS, LLC.
39783  *
39784  * Originally Released Under LGPL - original licence link has changed is not relivant.
39785  *
39786  * Fork - LGPL
39787  * <script type="text/javascript">
39788  */
39789 /**
39790  * @class Roo.menu.Item
39791  * @extends Roo.menu.BaseItem
39792  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39793  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39794  * activation and click handling.
39795  * @constructor
39796  * Creates a new Item
39797  * @param {Object} config Configuration options
39798  */
39799 Roo.menu.Item = function(config){
39800     Roo.menu.Item.superclass.constructor.call(this, config);
39801     if(this.menu){
39802         this.menu = Roo.menu.MenuMgr.get(this.menu);
39803     }
39804 };
39805 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39806     /**
39807      * @cfg {Roo.menu.Menu} menu
39808      * A Sub menu
39809      */
39810     /**
39811      * @cfg {String} text
39812      * The text to show on the menu item.
39813      */
39814     text: '',
39815      /**
39816      * @cfg {String} HTML to render in menu
39817      * The text to show on the menu item (HTML version).
39818      */
39819     html: '',
39820     /**
39821      * @cfg {String} icon
39822      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39823      */
39824     icon: undefined,
39825     /**
39826      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39827      */
39828     itemCls : "x-menu-item",
39829     /**
39830      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39831      */
39832     canActivate : true,
39833     /**
39834      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39835      */
39836     showDelay: 200,
39837     // doc'd in BaseItem
39838     hideDelay: 200,
39839
39840     // private
39841     ctype: "Roo.menu.Item",
39842     
39843     // private
39844     onRender : function(container, position){
39845         var el = document.createElement("a");
39846         el.hideFocus = true;
39847         el.unselectable = "on";
39848         el.href = this.href || "#";
39849         if(this.hrefTarget){
39850             el.target = this.hrefTarget;
39851         }
39852         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39853         
39854         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39855         
39856         el.innerHTML = String.format(
39857                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39858                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39859         this.el = el;
39860         Roo.menu.Item.superclass.onRender.call(this, container, position);
39861     },
39862
39863     /**
39864      * Sets the text to display in this menu item
39865      * @param {String} text The text to display
39866      * @param {Boolean} isHTML true to indicate text is pure html.
39867      */
39868     setText : function(text, isHTML){
39869         if (isHTML) {
39870             this.html = text;
39871         } else {
39872             this.text = text;
39873             this.html = '';
39874         }
39875         if(this.rendered){
39876             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39877      
39878             this.el.update(String.format(
39879                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39880                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39881             this.parentMenu.autoWidth();
39882         }
39883     },
39884
39885     // private
39886     handleClick : function(e){
39887         if(!this.href){ // if no link defined, stop the event automatically
39888             e.stopEvent();
39889         }
39890         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39891     },
39892
39893     // private
39894     activate : function(autoExpand){
39895         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39896             this.focus();
39897             if(autoExpand){
39898                 this.expandMenu();
39899             }
39900         }
39901         return true;
39902     },
39903
39904     // private
39905     shouldDeactivate : function(e){
39906         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39907             if(this.menu && this.menu.isVisible()){
39908                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39909             }
39910             return true;
39911         }
39912         return false;
39913     },
39914
39915     // private
39916     deactivate : function(){
39917         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39918         this.hideMenu();
39919     },
39920
39921     // private
39922     expandMenu : function(autoActivate){
39923         if(!this.disabled && this.menu){
39924             clearTimeout(this.hideTimer);
39925             delete this.hideTimer;
39926             if(!this.menu.isVisible() && !this.showTimer){
39927                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39928             }else if (this.menu.isVisible() && autoActivate){
39929                 this.menu.tryActivate(0, 1);
39930             }
39931         }
39932     },
39933
39934     // private
39935     deferExpand : function(autoActivate){
39936         delete this.showTimer;
39937         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39938         if(autoActivate){
39939             this.menu.tryActivate(0, 1);
39940         }
39941     },
39942
39943     // private
39944     hideMenu : function(){
39945         clearTimeout(this.showTimer);
39946         delete this.showTimer;
39947         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39948             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39949         }
39950     },
39951
39952     // private
39953     deferHide : function(){
39954         delete this.hideTimer;
39955         this.menu.hide();
39956     }
39957 });/*
39958  * Based on:
39959  * Ext JS Library 1.1.1
39960  * Copyright(c) 2006-2007, Ext JS, LLC.
39961  *
39962  * Originally Released Under LGPL - original licence link has changed is not relivant.
39963  *
39964  * Fork - LGPL
39965  * <script type="text/javascript">
39966  */
39967  
39968 /**
39969  * @class Roo.menu.CheckItem
39970  * @extends Roo.menu.Item
39971  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39972  * @constructor
39973  * Creates a new CheckItem
39974  * @param {Object} config Configuration options
39975  */
39976 Roo.menu.CheckItem = function(config){
39977     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39978     this.addEvents({
39979         /**
39980          * @event beforecheckchange
39981          * Fires before the checked value is set, providing an opportunity to cancel if needed
39982          * @param {Roo.menu.CheckItem} this
39983          * @param {Boolean} checked The new checked value that will be set
39984          */
39985         "beforecheckchange" : true,
39986         /**
39987          * @event checkchange
39988          * Fires after the checked value has been set
39989          * @param {Roo.menu.CheckItem} this
39990          * @param {Boolean} checked The checked value that was set
39991          */
39992         "checkchange" : true
39993     });
39994     if(this.checkHandler){
39995         this.on('checkchange', this.checkHandler, this.scope);
39996     }
39997 };
39998 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39999     /**
40000      * @cfg {String} group
40001      * All check items with the same group name will automatically be grouped into a single-select
40002      * radio button group (defaults to '')
40003      */
40004     /**
40005      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40006      */
40007     itemCls : "x-menu-item x-menu-check-item",
40008     /**
40009      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40010      */
40011     groupClass : "x-menu-group-item",
40012
40013     /**
40014      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40015      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40016      * initialized with checked = true will be rendered as checked.
40017      */
40018     checked: false,
40019
40020     // private
40021     ctype: "Roo.menu.CheckItem",
40022
40023     // private
40024     onRender : function(c){
40025         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40026         if(this.group){
40027             this.el.addClass(this.groupClass);
40028         }
40029         Roo.menu.MenuMgr.registerCheckable(this);
40030         if(this.checked){
40031             this.checked = false;
40032             this.setChecked(true, true);
40033         }
40034     },
40035
40036     // private
40037     destroy : function(){
40038         if(this.rendered){
40039             Roo.menu.MenuMgr.unregisterCheckable(this);
40040         }
40041         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40042     },
40043
40044     /**
40045      * Set the checked state of this item
40046      * @param {Boolean} checked The new checked value
40047      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40048      */
40049     setChecked : function(state, suppressEvent){
40050         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40051             if(this.container){
40052                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40053             }
40054             this.checked = state;
40055             if(suppressEvent !== true){
40056                 this.fireEvent("checkchange", this, state);
40057             }
40058         }
40059     },
40060
40061     // private
40062     handleClick : function(e){
40063        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40064            this.setChecked(!this.checked);
40065        }
40066        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40067     }
40068 });/*
40069  * Based on:
40070  * Ext JS Library 1.1.1
40071  * Copyright(c) 2006-2007, Ext JS, LLC.
40072  *
40073  * Originally Released Under LGPL - original licence link has changed is not relivant.
40074  *
40075  * Fork - LGPL
40076  * <script type="text/javascript">
40077  */
40078  
40079 /**
40080  * @class Roo.menu.DateItem
40081  * @extends Roo.menu.Adapter
40082  * A menu item that wraps the {@link Roo.DatPicker} component.
40083  * @constructor
40084  * Creates a new DateItem
40085  * @param {Object} config Configuration options
40086  */
40087 Roo.menu.DateItem = function(config){
40088     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40089     /** The Roo.DatePicker object @type Roo.DatePicker */
40090     this.picker = this.component;
40091     this.addEvents({select: true});
40092     
40093     this.picker.on("render", function(picker){
40094         picker.getEl().swallowEvent("click");
40095         picker.container.addClass("x-menu-date-item");
40096     });
40097
40098     this.picker.on("select", this.onSelect, this);
40099 };
40100
40101 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40102     // private
40103     onSelect : function(picker, date){
40104         this.fireEvent("select", this, date, picker);
40105         Roo.menu.DateItem.superclass.handleClick.call(this);
40106     }
40107 });/*
40108  * Based on:
40109  * Ext JS Library 1.1.1
40110  * Copyright(c) 2006-2007, Ext JS, LLC.
40111  *
40112  * Originally Released Under LGPL - original licence link has changed is not relivant.
40113  *
40114  * Fork - LGPL
40115  * <script type="text/javascript">
40116  */
40117  
40118 /**
40119  * @class Roo.menu.ColorItem
40120  * @extends Roo.menu.Adapter
40121  * A menu item that wraps the {@link Roo.ColorPalette} component.
40122  * @constructor
40123  * Creates a new ColorItem
40124  * @param {Object} config Configuration options
40125  */
40126 Roo.menu.ColorItem = function(config){
40127     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40128     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40129     this.palette = this.component;
40130     this.relayEvents(this.palette, ["select"]);
40131     if(this.selectHandler){
40132         this.on('select', this.selectHandler, this.scope);
40133     }
40134 };
40135 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40136  * Based on:
40137  * Ext JS Library 1.1.1
40138  * Copyright(c) 2006-2007, Ext JS, LLC.
40139  *
40140  * Originally Released Under LGPL - original licence link has changed is not relivant.
40141  *
40142  * Fork - LGPL
40143  * <script type="text/javascript">
40144  */
40145  
40146
40147 /**
40148  * @class Roo.menu.DateMenu
40149  * @extends Roo.menu.Menu
40150  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40151  * @constructor
40152  * Creates a new DateMenu
40153  * @param {Object} config Configuration options
40154  */
40155 Roo.menu.DateMenu = function(config){
40156     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40157     this.plain = true;
40158     var di = new Roo.menu.DateItem(config);
40159     this.add(di);
40160     /**
40161      * The {@link Roo.DatePicker} instance for this DateMenu
40162      * @type DatePicker
40163      */
40164     this.picker = di.picker;
40165     /**
40166      * @event select
40167      * @param {DatePicker} picker
40168      * @param {Date} date
40169      */
40170     this.relayEvents(di, ["select"]);
40171     this.on('beforeshow', function(){
40172         if(this.picker){
40173             this.picker.hideMonthPicker(false);
40174         }
40175     }, this);
40176 };
40177 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40178     cls:'x-date-menu'
40179 });/*
40180  * Based on:
40181  * Ext JS Library 1.1.1
40182  * Copyright(c) 2006-2007, Ext JS, LLC.
40183  *
40184  * Originally Released Under LGPL - original licence link has changed is not relivant.
40185  *
40186  * Fork - LGPL
40187  * <script type="text/javascript">
40188  */
40189  
40190
40191 /**
40192  * @class Roo.menu.ColorMenu
40193  * @extends Roo.menu.Menu
40194  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40195  * @constructor
40196  * Creates a new ColorMenu
40197  * @param {Object} config Configuration options
40198  */
40199 Roo.menu.ColorMenu = function(config){
40200     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40201     this.plain = true;
40202     var ci = new Roo.menu.ColorItem(config);
40203     this.add(ci);
40204     /**
40205      * The {@link Roo.ColorPalette} instance for this ColorMenu
40206      * @type ColorPalette
40207      */
40208     this.palette = ci.palette;
40209     /**
40210      * @event select
40211      * @param {ColorPalette} palette
40212      * @param {String} color
40213      */
40214     this.relayEvents(ci, ["select"]);
40215 };
40216 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40217  * Based on:
40218  * Ext JS Library 1.1.1
40219  * Copyright(c) 2006-2007, Ext JS, LLC.
40220  *
40221  * Originally Released Under LGPL - original licence link has changed is not relivant.
40222  *
40223  * Fork - LGPL
40224  * <script type="text/javascript">
40225  */
40226  
40227 /**
40228  * @class Roo.form.TextItem
40229  * @extends Roo.BoxComponent
40230  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40231  * @constructor
40232  * Creates a new TextItem
40233  * @param {Object} config Configuration options
40234  */
40235 Roo.form.TextItem = function(config){
40236     Roo.form.TextItem.superclass.constructor.call(this, config);
40237 };
40238
40239 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40240     
40241     /**
40242      * @cfg {String} tag the tag for this item (default div)
40243      */
40244     tag : 'div',
40245     /**
40246      * @cfg {String} html the content for this item
40247      */
40248     html : '',
40249     
40250     getAutoCreate : function()
40251     {
40252         var cfg = {
40253             id: this.id,
40254             tag: this.tag,
40255             html: this.html,
40256             cls: 'x-form-item'
40257         };
40258         
40259         return cfg;
40260         
40261     },
40262     
40263     onRender : function(ct, position)
40264     {
40265         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40266         
40267         if(!this.el){
40268             var cfg = this.getAutoCreate();
40269             if(!cfg.name){
40270                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40271             }
40272             if (!cfg.name.length) {
40273                 delete cfg.name;
40274             }
40275             this.el = ct.createChild(cfg, position);
40276         }
40277     },
40278     /*
40279      * setHTML
40280      * @param {String} html update the Contents of the element.
40281      */
40282     setHTML : function(html)
40283     {
40284         this.fieldEl.dom.innerHTML = html;
40285     }
40286     
40287 });/*
40288  * Based on:
40289  * Ext JS Library 1.1.1
40290  * Copyright(c) 2006-2007, Ext JS, LLC.
40291  *
40292  * Originally Released Under LGPL - original licence link has changed is not relivant.
40293  *
40294  * Fork - LGPL
40295  * <script type="text/javascript">
40296  */
40297  
40298 /**
40299  * @class Roo.form.Field
40300  * @extends Roo.BoxComponent
40301  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40302  * @constructor
40303  * Creates a new Field
40304  * @param {Object} config Configuration options
40305  */
40306 Roo.form.Field = function(config){
40307     Roo.form.Field.superclass.constructor.call(this, config);
40308 };
40309
40310 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40311     /**
40312      * @cfg {String} fieldLabel Label to use when rendering a form.
40313      */
40314        /**
40315      * @cfg {String} qtip Mouse over tip
40316      */
40317      
40318     /**
40319      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40320      */
40321     invalidClass : "x-form-invalid",
40322     /**
40323      * @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")
40324      */
40325     invalidText : "The value in this field is invalid",
40326     /**
40327      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40328      */
40329     focusClass : "x-form-focus",
40330     /**
40331      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40332       automatic validation (defaults to "keyup").
40333      */
40334     validationEvent : "keyup",
40335     /**
40336      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40337      */
40338     validateOnBlur : true,
40339     /**
40340      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40341      */
40342     validationDelay : 250,
40343     /**
40344      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40345      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40346      */
40347     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40348     /**
40349      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40350      */
40351     fieldClass : "x-form-field",
40352     /**
40353      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40354      *<pre>
40355 Value         Description
40356 -----------   ----------------------------------------------------------------------
40357 qtip          Display a quick tip when the user hovers over the field
40358 title         Display a default browser title attribute popup
40359 under         Add a block div beneath the field containing the error text
40360 side          Add an error icon to the right of the field with a popup on hover
40361 [element id]  Add the error text directly to the innerHTML of the specified element
40362 </pre>
40363      */
40364     msgTarget : 'qtip',
40365     /**
40366      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40367      */
40368     msgFx : 'normal',
40369
40370     /**
40371      * @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.
40372      */
40373     readOnly : false,
40374
40375     /**
40376      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40377      */
40378     disabled : false,
40379
40380     /**
40381      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40382      */
40383     inputType : undefined,
40384     
40385     /**
40386      * @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).
40387          */
40388         tabIndex : undefined,
40389         
40390     // private
40391     isFormField : true,
40392
40393     // private
40394     hasFocus : false,
40395     /**
40396      * @property {Roo.Element} fieldEl
40397      * Element Containing the rendered Field (with label etc.)
40398      */
40399     /**
40400      * @cfg {Mixed} value A value to initialize this field with.
40401      */
40402     value : undefined,
40403
40404     /**
40405      * @cfg {String} name The field's HTML name attribute.
40406      */
40407     /**
40408      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40409      */
40410     // private
40411     loadedValue : false,
40412      
40413      
40414         // private ??
40415         initComponent : function(){
40416         Roo.form.Field.superclass.initComponent.call(this);
40417         this.addEvents({
40418             /**
40419              * @event focus
40420              * Fires when this field receives input focus.
40421              * @param {Roo.form.Field} this
40422              */
40423             focus : true,
40424             /**
40425              * @event blur
40426              * Fires when this field loses input focus.
40427              * @param {Roo.form.Field} this
40428              */
40429             blur : true,
40430             /**
40431              * @event specialkey
40432              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40433              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40434              * @param {Roo.form.Field} this
40435              * @param {Roo.EventObject} e The event object
40436              */
40437             specialkey : true,
40438             /**
40439              * @event change
40440              * Fires just before the field blurs if the field value has changed.
40441              * @param {Roo.form.Field} this
40442              * @param {Mixed} newValue The new value
40443              * @param {Mixed} oldValue The original value
40444              */
40445             change : true,
40446             /**
40447              * @event invalid
40448              * Fires after the field has been marked as invalid.
40449              * @param {Roo.form.Field} this
40450              * @param {String} msg The validation message
40451              */
40452             invalid : true,
40453             /**
40454              * @event valid
40455              * Fires after the field has been validated with no errors.
40456              * @param {Roo.form.Field} this
40457              */
40458             valid : true,
40459              /**
40460              * @event keyup
40461              * Fires after the key up
40462              * @param {Roo.form.Field} this
40463              * @param {Roo.EventObject}  e The event Object
40464              */
40465             keyup : true
40466         });
40467     },
40468
40469     /**
40470      * Returns the name attribute of the field if available
40471      * @return {String} name The field name
40472      */
40473     getName: function(){
40474          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40475     },
40476
40477     // private
40478     onRender : function(ct, position){
40479         Roo.form.Field.superclass.onRender.call(this, ct, position);
40480         if(!this.el){
40481             var cfg = this.getAutoCreate();
40482             if(!cfg.name){
40483                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40484             }
40485             if (!cfg.name.length) {
40486                 delete cfg.name;
40487             }
40488             if(this.inputType){
40489                 cfg.type = this.inputType;
40490             }
40491             this.el = ct.createChild(cfg, position);
40492         }
40493         var type = this.el.dom.type;
40494         if(type){
40495             if(type == 'password'){
40496                 type = 'text';
40497             }
40498             this.el.addClass('x-form-'+type);
40499         }
40500         if(this.readOnly){
40501             this.el.dom.readOnly = true;
40502         }
40503         if(this.tabIndex !== undefined){
40504             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40505         }
40506
40507         this.el.addClass([this.fieldClass, this.cls]);
40508         this.initValue();
40509     },
40510
40511     /**
40512      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40513      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40514      * @return {Roo.form.Field} this
40515      */
40516     applyTo : function(target){
40517         this.allowDomMove = false;
40518         this.el = Roo.get(target);
40519         this.render(this.el.dom.parentNode);
40520         return this;
40521     },
40522
40523     // private
40524     initValue : function(){
40525         if(this.value !== undefined){
40526             this.setValue(this.value);
40527         }else if(this.el.dom.value.length > 0){
40528             this.setValue(this.el.dom.value);
40529         }
40530     },
40531
40532     /**
40533      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40534      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40535      */
40536     isDirty : function() {
40537         if(this.disabled) {
40538             return false;
40539         }
40540         return String(this.getValue()) !== String(this.originalValue);
40541     },
40542
40543     /**
40544      * stores the current value in loadedValue
40545      */
40546     resetHasChanged : function()
40547     {
40548         this.loadedValue = String(this.getValue());
40549     },
40550     /**
40551      * checks the current value against the 'loaded' value.
40552      * Note - will return false if 'resetHasChanged' has not been called first.
40553      */
40554     hasChanged : function()
40555     {
40556         if(this.disabled || this.readOnly) {
40557             return false;
40558         }
40559         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40560     },
40561     
40562     
40563     
40564     // private
40565     afterRender : function(){
40566         Roo.form.Field.superclass.afterRender.call(this);
40567         this.initEvents();
40568     },
40569
40570     // private
40571     fireKey : function(e){
40572         //Roo.log('field ' + e.getKey());
40573         if(e.isNavKeyPress()){
40574             this.fireEvent("specialkey", this, e);
40575         }
40576     },
40577
40578     /**
40579      * Resets the current field value to the originally loaded value and clears any validation messages
40580      */
40581     reset : function(){
40582         this.setValue(this.resetValue);
40583         this.originalValue = this.getValue();
40584         this.clearInvalid();
40585     },
40586
40587     // private
40588     initEvents : function(){
40589         // safari killled keypress - so keydown is now used..
40590         this.el.on("keydown" , this.fireKey,  this);
40591         this.el.on("focus", this.onFocus,  this);
40592         this.el.on("blur", this.onBlur,  this);
40593         this.el.relayEvent('keyup', this);
40594
40595         // reference to original value for reset
40596         this.originalValue = this.getValue();
40597         this.resetValue =  this.getValue();
40598     },
40599
40600     // private
40601     onFocus : function(){
40602         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40603             this.el.addClass(this.focusClass);
40604         }
40605         if(!this.hasFocus){
40606             this.hasFocus = true;
40607             this.startValue = this.getValue();
40608             this.fireEvent("focus", this);
40609         }
40610     },
40611
40612     beforeBlur : Roo.emptyFn,
40613
40614     // private
40615     onBlur : function(){
40616         this.beforeBlur();
40617         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40618             this.el.removeClass(this.focusClass);
40619         }
40620         this.hasFocus = false;
40621         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40622             this.validate();
40623         }
40624         var v = this.getValue();
40625         if(String(v) !== String(this.startValue)){
40626             this.fireEvent('change', this, v, this.startValue);
40627         }
40628         this.fireEvent("blur", this);
40629     },
40630
40631     /**
40632      * Returns whether or not the field value is currently valid
40633      * @param {Boolean} preventMark True to disable marking the field invalid
40634      * @return {Boolean} True if the value is valid, else false
40635      */
40636     isValid : function(preventMark){
40637         if(this.disabled){
40638             return true;
40639         }
40640         var restore = this.preventMark;
40641         this.preventMark = preventMark === true;
40642         var v = this.validateValue(this.processValue(this.getRawValue()));
40643         this.preventMark = restore;
40644         return v;
40645     },
40646
40647     /**
40648      * Validates the field value
40649      * @return {Boolean} True if the value is valid, else false
40650      */
40651     validate : function(){
40652         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40653             this.clearInvalid();
40654             return true;
40655         }
40656         return false;
40657     },
40658
40659     processValue : function(value){
40660         return value;
40661     },
40662
40663     // private
40664     // Subclasses should provide the validation implementation by overriding this
40665     validateValue : function(value){
40666         return true;
40667     },
40668
40669     /**
40670      * Mark this field as invalid
40671      * @param {String} msg The validation message
40672      */
40673     markInvalid : function(msg){
40674         if(!this.rendered || this.preventMark){ // not rendered
40675             return;
40676         }
40677         
40678         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40679         
40680         obj.el.addClass(this.invalidClass);
40681         msg = msg || this.invalidText;
40682         switch(this.msgTarget){
40683             case 'qtip':
40684                 obj.el.dom.qtip = msg;
40685                 obj.el.dom.qclass = 'x-form-invalid-tip';
40686                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40687                     Roo.QuickTips.enable();
40688                 }
40689                 break;
40690             case 'title':
40691                 this.el.dom.title = msg;
40692                 break;
40693             case 'under':
40694                 if(!this.errorEl){
40695                     var elp = this.el.findParent('.x-form-element', 5, true);
40696                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40697                     this.errorEl.setWidth(elp.getWidth(true)-20);
40698                 }
40699                 this.errorEl.update(msg);
40700                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40701                 break;
40702             case 'side':
40703                 if(!this.errorIcon){
40704                     var elp = this.el.findParent('.x-form-element', 5, true);
40705                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40706                 }
40707                 this.alignErrorIcon();
40708                 this.errorIcon.dom.qtip = msg;
40709                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40710                 this.errorIcon.show();
40711                 this.on('resize', this.alignErrorIcon, this);
40712                 break;
40713             default:
40714                 var t = Roo.getDom(this.msgTarget);
40715                 t.innerHTML = msg;
40716                 t.style.display = this.msgDisplay;
40717                 break;
40718         }
40719         this.fireEvent('invalid', this, msg);
40720     },
40721
40722     // private
40723     alignErrorIcon : function(){
40724         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40725     },
40726
40727     /**
40728      * Clear any invalid styles/messages for this field
40729      */
40730     clearInvalid : function(){
40731         if(!this.rendered || this.preventMark){ // not rendered
40732             return;
40733         }
40734         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40735         
40736         obj.el.removeClass(this.invalidClass);
40737         switch(this.msgTarget){
40738             case 'qtip':
40739                 obj.el.dom.qtip = '';
40740                 break;
40741             case 'title':
40742                 this.el.dom.title = '';
40743                 break;
40744             case 'under':
40745                 if(this.errorEl){
40746                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40747                 }
40748                 break;
40749             case 'side':
40750                 if(this.errorIcon){
40751                     this.errorIcon.dom.qtip = '';
40752                     this.errorIcon.hide();
40753                     this.un('resize', this.alignErrorIcon, this);
40754                 }
40755                 break;
40756             default:
40757                 var t = Roo.getDom(this.msgTarget);
40758                 t.innerHTML = '';
40759                 t.style.display = 'none';
40760                 break;
40761         }
40762         this.fireEvent('valid', this);
40763     },
40764
40765     /**
40766      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40767      * @return {Mixed} value The field value
40768      */
40769     getRawValue : function(){
40770         var v = this.el.getValue();
40771         
40772         return v;
40773     },
40774
40775     /**
40776      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40777      * @return {Mixed} value The field value
40778      */
40779     getValue : function(){
40780         var v = this.el.getValue();
40781          
40782         return v;
40783     },
40784
40785     /**
40786      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40787      * @param {Mixed} value The value to set
40788      */
40789     setRawValue : function(v){
40790         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40791     },
40792
40793     /**
40794      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40795      * @param {Mixed} value The value to set
40796      */
40797     setValue : function(v){
40798         this.value = v;
40799         if(this.rendered){
40800             this.el.dom.value = (v === null || v === undefined ? '' : v);
40801              this.validate();
40802         }
40803     },
40804
40805     adjustSize : function(w, h){
40806         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40807         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40808         return s;
40809     },
40810
40811     adjustWidth : function(tag, w){
40812         tag = tag.toLowerCase();
40813         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40814             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40815                 if(tag == 'input'){
40816                     return w + 2;
40817                 }
40818                 if(tag == 'textarea'){
40819                     return w-2;
40820                 }
40821             }else if(Roo.isOpera){
40822                 if(tag == 'input'){
40823                     return w + 2;
40824                 }
40825                 if(tag == 'textarea'){
40826                     return w-2;
40827                 }
40828             }
40829         }
40830         return w;
40831     }
40832 });
40833
40834
40835 // anything other than normal should be considered experimental
40836 Roo.form.Field.msgFx = {
40837     normal : {
40838         show: function(msgEl, f){
40839             msgEl.setDisplayed('block');
40840         },
40841
40842         hide : function(msgEl, f){
40843             msgEl.setDisplayed(false).update('');
40844         }
40845     },
40846
40847     slide : {
40848         show: function(msgEl, f){
40849             msgEl.slideIn('t', {stopFx:true});
40850         },
40851
40852         hide : function(msgEl, f){
40853             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40854         }
40855     },
40856
40857     slideRight : {
40858         show: function(msgEl, f){
40859             msgEl.fixDisplay();
40860             msgEl.alignTo(f.el, 'tl-tr');
40861             msgEl.slideIn('l', {stopFx:true});
40862         },
40863
40864         hide : function(msgEl, f){
40865             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40866         }
40867     }
40868 };/*
40869  * Based on:
40870  * Ext JS Library 1.1.1
40871  * Copyright(c) 2006-2007, Ext JS, LLC.
40872  *
40873  * Originally Released Under LGPL - original licence link has changed is not relivant.
40874  *
40875  * Fork - LGPL
40876  * <script type="text/javascript">
40877  */
40878  
40879
40880 /**
40881  * @class Roo.form.TextField
40882  * @extends Roo.form.Field
40883  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40884  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40885  * @constructor
40886  * Creates a new TextField
40887  * @param {Object} config Configuration options
40888  */
40889 Roo.form.TextField = function(config){
40890     Roo.form.TextField.superclass.constructor.call(this, config);
40891     this.addEvents({
40892         /**
40893          * @event autosize
40894          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40895          * according to the default logic, but this event provides a hook for the developer to apply additional
40896          * logic at runtime to resize the field if needed.
40897              * @param {Roo.form.Field} this This text field
40898              * @param {Number} width The new field width
40899              */
40900         autosize : true
40901     });
40902 };
40903
40904 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40905     /**
40906      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40907      */
40908     grow : false,
40909     /**
40910      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40911      */
40912     growMin : 30,
40913     /**
40914      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40915      */
40916     growMax : 800,
40917     /**
40918      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40919      */
40920     vtype : null,
40921     /**
40922      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40923      */
40924     maskRe : null,
40925     /**
40926      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40927      */
40928     disableKeyFilter : false,
40929     /**
40930      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40931      */
40932     allowBlank : true,
40933     /**
40934      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40935      */
40936     minLength : 0,
40937     /**
40938      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40939      */
40940     maxLength : Number.MAX_VALUE,
40941     /**
40942      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40943      */
40944     minLengthText : "The minimum length for this field is {0}",
40945     /**
40946      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40947      */
40948     maxLengthText : "The maximum length for this field is {0}",
40949     /**
40950      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40951      */
40952     selectOnFocus : false,
40953     /**
40954      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40955      */    
40956     allowLeadingSpace : false,
40957     /**
40958      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40959      */
40960     blankText : "This field is required",
40961     /**
40962      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40963      * If available, this function will be called only after the basic validators all return true, and will be passed the
40964      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40965      */
40966     validator : null,
40967     /**
40968      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40969      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40970      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40971      */
40972     regex : null,
40973     /**
40974      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40975      */
40976     regexText : "",
40977     /**
40978      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40979      */
40980     emptyText : null,
40981    
40982
40983     // private
40984     initEvents : function()
40985     {
40986         if (this.emptyText) {
40987             this.el.attr('placeholder', this.emptyText);
40988         }
40989         
40990         Roo.form.TextField.superclass.initEvents.call(this);
40991         if(this.validationEvent == 'keyup'){
40992             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40993             this.el.on('keyup', this.filterValidation, this);
40994         }
40995         else if(this.validationEvent !== false){
40996             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40997         }
40998         
40999         if(this.selectOnFocus){
41000             this.on("focus", this.preFocus, this);
41001         }
41002         if (!this.allowLeadingSpace) {
41003             this.on('blur', this.cleanLeadingSpace, this);
41004         }
41005         
41006         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41007             this.el.on("keypress", this.filterKeys, this);
41008         }
41009         if(this.grow){
41010             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41011             this.el.on("click", this.autoSize,  this);
41012         }
41013         if(this.el.is('input[type=password]') && Roo.isSafari){
41014             this.el.on('keydown', this.SafariOnKeyDown, this);
41015         }
41016     },
41017
41018     processValue : function(value){
41019         if(this.stripCharsRe){
41020             var newValue = value.replace(this.stripCharsRe, '');
41021             if(newValue !== value){
41022                 this.setRawValue(newValue);
41023                 return newValue;
41024             }
41025         }
41026         return value;
41027     },
41028
41029     filterValidation : function(e){
41030         if(!e.isNavKeyPress()){
41031             this.validationTask.delay(this.validationDelay);
41032         }
41033     },
41034
41035     // private
41036     onKeyUp : function(e){
41037         if(!e.isNavKeyPress()){
41038             this.autoSize();
41039         }
41040     },
41041     // private - clean the leading white space
41042     cleanLeadingSpace : function(e)
41043     {
41044         if ( this.inputType == 'file') {
41045             return;
41046         }
41047         
41048         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41049     },
41050     /**
41051      * Resets the current field value to the originally-loaded value and clears any validation messages.
41052      *  
41053      */
41054     reset : function(){
41055         Roo.form.TextField.superclass.reset.call(this);
41056        
41057     }, 
41058     // private
41059     preFocus : function(){
41060         
41061         if(this.selectOnFocus){
41062             this.el.dom.select();
41063         }
41064     },
41065
41066     
41067     // private
41068     filterKeys : function(e){
41069         var k = e.getKey();
41070         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41071             return;
41072         }
41073         var c = e.getCharCode(), cc = String.fromCharCode(c);
41074         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41075             return;
41076         }
41077         if(!this.maskRe.test(cc)){
41078             e.stopEvent();
41079         }
41080     },
41081
41082     setValue : function(v){
41083         
41084         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41085         
41086         this.autoSize();
41087     },
41088
41089     /**
41090      * Validates a value according to the field's validation rules and marks the field as invalid
41091      * if the validation fails
41092      * @param {Mixed} value The value to validate
41093      * @return {Boolean} True if the value is valid, else false
41094      */
41095     validateValue : function(value){
41096         if(value.length < 1)  { // if it's blank
41097              if(this.allowBlank){
41098                 this.clearInvalid();
41099                 return true;
41100              }else{
41101                 this.markInvalid(this.blankText);
41102                 return false;
41103              }
41104         }
41105         if(value.length < this.minLength){
41106             this.markInvalid(String.format(this.minLengthText, this.minLength));
41107             return false;
41108         }
41109         if(value.length > this.maxLength){
41110             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41111             return false;
41112         }
41113         if(this.vtype){
41114             var vt = Roo.form.VTypes;
41115             if(!vt[this.vtype](value, this)){
41116                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41117                 return false;
41118             }
41119         }
41120         if(typeof this.validator == "function"){
41121             var msg = this.validator(value);
41122             if(msg !== true){
41123                 this.markInvalid(msg);
41124                 return false;
41125             }
41126         }
41127         if(this.regex && !this.regex.test(value)){
41128             this.markInvalid(this.regexText);
41129             return false;
41130         }
41131         return true;
41132     },
41133
41134     /**
41135      * Selects text in this field
41136      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41137      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41138      */
41139     selectText : function(start, end){
41140         var v = this.getRawValue();
41141         if(v.length > 0){
41142             start = start === undefined ? 0 : start;
41143             end = end === undefined ? v.length : end;
41144             var d = this.el.dom;
41145             if(d.setSelectionRange){
41146                 d.setSelectionRange(start, end);
41147             }else if(d.createTextRange){
41148                 var range = d.createTextRange();
41149                 range.moveStart("character", start);
41150                 range.moveEnd("character", v.length-end);
41151                 range.select();
41152             }
41153         }
41154     },
41155
41156     /**
41157      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41158      * This only takes effect if grow = true, and fires the autosize event.
41159      */
41160     autoSize : function(){
41161         if(!this.grow || !this.rendered){
41162             return;
41163         }
41164         if(!this.metrics){
41165             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41166         }
41167         var el = this.el;
41168         var v = el.dom.value;
41169         var d = document.createElement('div');
41170         d.appendChild(document.createTextNode(v));
41171         v = d.innerHTML;
41172         d = null;
41173         v += "&#160;";
41174         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41175         this.el.setWidth(w);
41176         this.fireEvent("autosize", this, w);
41177     },
41178     
41179     // private
41180     SafariOnKeyDown : function(event)
41181     {
41182         // this is a workaround for a password hang bug on chrome/ webkit.
41183         
41184         var isSelectAll = false;
41185         
41186         if(this.el.dom.selectionEnd > 0){
41187             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41188         }
41189         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41190             event.preventDefault();
41191             this.setValue('');
41192             return;
41193         }
41194         
41195         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41196             
41197             event.preventDefault();
41198             // this is very hacky as keydown always get's upper case.
41199             
41200             var cc = String.fromCharCode(event.getCharCode());
41201             
41202             
41203             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41204             
41205         }
41206         
41207         
41208     }
41209 });/*
41210  * Based on:
41211  * Ext JS Library 1.1.1
41212  * Copyright(c) 2006-2007, Ext JS, LLC.
41213  *
41214  * Originally Released Under LGPL - original licence link has changed is not relivant.
41215  *
41216  * Fork - LGPL
41217  * <script type="text/javascript">
41218  */
41219  
41220 /**
41221  * @class Roo.form.Hidden
41222  * @extends Roo.form.TextField
41223  * Simple Hidden element used on forms 
41224  * 
41225  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41226  * 
41227  * @constructor
41228  * Creates a new Hidden form element.
41229  * @param {Object} config Configuration options
41230  */
41231
41232
41233
41234 // easy hidden field...
41235 Roo.form.Hidden = function(config){
41236     Roo.form.Hidden.superclass.constructor.call(this, config);
41237 };
41238   
41239 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41240     fieldLabel:      '',
41241     inputType:      'hidden',
41242     width:          50,
41243     allowBlank:     true,
41244     labelSeparator: '',
41245     hidden:         true,
41246     itemCls :       'x-form-item-display-none'
41247
41248
41249 });
41250
41251
41252 /*
41253  * Based on:
41254  * Ext JS Library 1.1.1
41255  * Copyright(c) 2006-2007, Ext JS, LLC.
41256  *
41257  * Originally Released Under LGPL - original licence link has changed is not relivant.
41258  *
41259  * Fork - LGPL
41260  * <script type="text/javascript">
41261  */
41262  
41263 /**
41264  * @class Roo.form.TriggerField
41265  * @extends Roo.form.TextField
41266  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41267  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41268  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41269  * for which you can provide a custom implementation.  For example:
41270  * <pre><code>
41271 var trigger = new Roo.form.TriggerField();
41272 trigger.onTriggerClick = myTriggerFn;
41273 trigger.applyTo('my-field');
41274 </code></pre>
41275  *
41276  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41277  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41278  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41279  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41280  * @constructor
41281  * Create a new TriggerField.
41282  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41283  * to the base TextField)
41284  */
41285 Roo.form.TriggerField = function(config){
41286     this.mimicing = false;
41287     Roo.form.TriggerField.superclass.constructor.call(this, config);
41288 };
41289
41290 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41291     /**
41292      * @cfg {String} triggerClass A CSS class to apply to the trigger
41293      */
41294     /**
41295      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41296      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41297      */
41298     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41299     /**
41300      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41301      */
41302     hideTrigger:false,
41303
41304     /** @cfg {Boolean} grow @hide */
41305     /** @cfg {Number} growMin @hide */
41306     /** @cfg {Number} growMax @hide */
41307
41308     /**
41309      * @hide 
41310      * @method
41311      */
41312     autoSize: Roo.emptyFn,
41313     // private
41314     monitorTab : true,
41315     // private
41316     deferHeight : true,
41317
41318     
41319     actionMode : 'wrap',
41320     // private
41321     onResize : function(w, h){
41322         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41323         if(typeof w == 'number'){
41324             var x = w - this.trigger.getWidth();
41325             this.el.setWidth(this.adjustWidth('input', x));
41326             this.trigger.setStyle('left', x+'px');
41327         }
41328     },
41329
41330     // private
41331     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41332
41333     // private
41334     getResizeEl : function(){
41335         return this.wrap;
41336     },
41337
41338     // private
41339     getPositionEl : function(){
41340         return this.wrap;
41341     },
41342
41343     // private
41344     alignErrorIcon : function(){
41345         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41346     },
41347
41348     // private
41349     onRender : function(ct, position){
41350         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41351         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41352         this.trigger = this.wrap.createChild(this.triggerConfig ||
41353                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41354         if(this.hideTrigger){
41355             this.trigger.setDisplayed(false);
41356         }
41357         this.initTrigger();
41358         if(!this.width){
41359             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41360         }
41361     },
41362
41363     // private
41364     initTrigger : function(){
41365         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41366         this.trigger.addClassOnOver('x-form-trigger-over');
41367         this.trigger.addClassOnClick('x-form-trigger-click');
41368     },
41369
41370     // private
41371     onDestroy : function(){
41372         if(this.trigger){
41373             this.trigger.removeAllListeners();
41374             this.trigger.remove();
41375         }
41376         if(this.wrap){
41377             this.wrap.remove();
41378         }
41379         Roo.form.TriggerField.superclass.onDestroy.call(this);
41380     },
41381
41382     // private
41383     onFocus : function(){
41384         Roo.form.TriggerField.superclass.onFocus.call(this);
41385         if(!this.mimicing){
41386             this.wrap.addClass('x-trigger-wrap-focus');
41387             this.mimicing = true;
41388             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41389             if(this.monitorTab){
41390                 this.el.on("keydown", this.checkTab, this);
41391             }
41392         }
41393     },
41394
41395     // private
41396     checkTab : function(e){
41397         if(e.getKey() == e.TAB){
41398             this.triggerBlur();
41399         }
41400     },
41401
41402     // private
41403     onBlur : function(){
41404         // do nothing
41405     },
41406
41407     // private
41408     mimicBlur : function(e, t){
41409         if(!this.wrap.contains(t) && this.validateBlur()){
41410             this.triggerBlur();
41411         }
41412     },
41413
41414     // private
41415     triggerBlur : function(){
41416         this.mimicing = false;
41417         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41418         if(this.monitorTab){
41419             this.el.un("keydown", this.checkTab, this);
41420         }
41421         this.wrap.removeClass('x-trigger-wrap-focus');
41422         Roo.form.TriggerField.superclass.onBlur.call(this);
41423     },
41424
41425     // private
41426     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41427     validateBlur : function(e, t){
41428         return true;
41429     },
41430
41431     // private
41432     onDisable : function(){
41433         Roo.form.TriggerField.superclass.onDisable.call(this);
41434         if(this.wrap){
41435             this.wrap.addClass('x-item-disabled');
41436         }
41437     },
41438
41439     // private
41440     onEnable : function(){
41441         Roo.form.TriggerField.superclass.onEnable.call(this);
41442         if(this.wrap){
41443             this.wrap.removeClass('x-item-disabled');
41444         }
41445     },
41446
41447     // private
41448     onShow : function(){
41449         var ae = this.getActionEl();
41450         
41451         if(ae){
41452             ae.dom.style.display = '';
41453             ae.dom.style.visibility = 'visible';
41454         }
41455     },
41456
41457     // private
41458     
41459     onHide : function(){
41460         var ae = this.getActionEl();
41461         ae.dom.style.display = 'none';
41462     },
41463
41464     /**
41465      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41466      * by an implementing function.
41467      * @method
41468      * @param {EventObject} e
41469      */
41470     onTriggerClick : Roo.emptyFn
41471 });
41472
41473 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41474 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41475 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41476 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41477     initComponent : function(){
41478         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41479
41480         this.triggerConfig = {
41481             tag:'span', cls:'x-form-twin-triggers', cn:[
41482             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41483             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41484         ]};
41485     },
41486
41487     getTrigger : function(index){
41488         return this.triggers[index];
41489     },
41490
41491     initTrigger : function(){
41492         var ts = this.trigger.select('.x-form-trigger', true);
41493         this.wrap.setStyle('overflow', 'hidden');
41494         var triggerField = this;
41495         ts.each(function(t, all, index){
41496             t.hide = function(){
41497                 var w = triggerField.wrap.getWidth();
41498                 this.dom.style.display = 'none';
41499                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41500             };
41501             t.show = function(){
41502                 var w = triggerField.wrap.getWidth();
41503                 this.dom.style.display = '';
41504                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41505             };
41506             var triggerIndex = 'Trigger'+(index+1);
41507
41508             if(this['hide'+triggerIndex]){
41509                 t.dom.style.display = 'none';
41510             }
41511             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41512             t.addClassOnOver('x-form-trigger-over');
41513             t.addClassOnClick('x-form-trigger-click');
41514         }, this);
41515         this.triggers = ts.elements;
41516     },
41517
41518     onTrigger1Click : Roo.emptyFn,
41519     onTrigger2Click : Roo.emptyFn
41520 });/*
41521  * Based on:
41522  * Ext JS Library 1.1.1
41523  * Copyright(c) 2006-2007, Ext JS, LLC.
41524  *
41525  * Originally Released Under LGPL - original licence link has changed is not relivant.
41526  *
41527  * Fork - LGPL
41528  * <script type="text/javascript">
41529  */
41530  
41531 /**
41532  * @class Roo.form.TextArea
41533  * @extends Roo.form.TextField
41534  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41535  * support for auto-sizing.
41536  * @constructor
41537  * Creates a new TextArea
41538  * @param {Object} config Configuration options
41539  */
41540 Roo.form.TextArea = function(config){
41541     Roo.form.TextArea.superclass.constructor.call(this, config);
41542     // these are provided exchanges for backwards compat
41543     // minHeight/maxHeight were replaced by growMin/growMax to be
41544     // compatible with TextField growing config values
41545     if(this.minHeight !== undefined){
41546         this.growMin = this.minHeight;
41547     }
41548     if(this.maxHeight !== undefined){
41549         this.growMax = this.maxHeight;
41550     }
41551 };
41552
41553 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41554     /**
41555      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41556      */
41557     growMin : 60,
41558     /**
41559      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41560      */
41561     growMax: 1000,
41562     /**
41563      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41564      * in the field (equivalent to setting overflow: hidden, defaults to false)
41565      */
41566     preventScrollbars: false,
41567     /**
41568      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41569      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41570      */
41571
41572     // private
41573     onRender : function(ct, position){
41574         if(!this.el){
41575             this.defaultAutoCreate = {
41576                 tag: "textarea",
41577                 style:"width:300px;height:60px;",
41578                 autocomplete: "new-password"
41579             };
41580         }
41581         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41582         if(this.grow){
41583             this.textSizeEl = Roo.DomHelper.append(document.body, {
41584                 tag: "pre", cls: "x-form-grow-sizer"
41585             });
41586             if(this.preventScrollbars){
41587                 this.el.setStyle("overflow", "hidden");
41588             }
41589             this.el.setHeight(this.growMin);
41590         }
41591     },
41592
41593     onDestroy : function(){
41594         if(this.textSizeEl){
41595             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41596         }
41597         Roo.form.TextArea.superclass.onDestroy.call(this);
41598     },
41599
41600     // private
41601     onKeyUp : function(e){
41602         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41603             this.autoSize();
41604         }
41605     },
41606
41607     /**
41608      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41609      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41610      */
41611     autoSize : function(){
41612         if(!this.grow || !this.textSizeEl){
41613             return;
41614         }
41615         var el = this.el;
41616         var v = el.dom.value;
41617         var ts = this.textSizeEl;
41618
41619         ts.innerHTML = '';
41620         ts.appendChild(document.createTextNode(v));
41621         v = ts.innerHTML;
41622
41623         Roo.fly(ts).setWidth(this.el.getWidth());
41624         if(v.length < 1){
41625             v = "&#160;&#160;";
41626         }else{
41627             if(Roo.isIE){
41628                 v = v.replace(/\n/g, '<p>&#160;</p>');
41629             }
41630             v += "&#160;\n&#160;";
41631         }
41632         ts.innerHTML = v;
41633         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41634         if(h != this.lastHeight){
41635             this.lastHeight = h;
41636             this.el.setHeight(h);
41637             this.fireEvent("autosize", this, h);
41638         }
41639     }
41640 });/*
41641  * Based on:
41642  * Ext JS Library 1.1.1
41643  * Copyright(c) 2006-2007, Ext JS, LLC.
41644  *
41645  * Originally Released Under LGPL - original licence link has changed is not relivant.
41646  *
41647  * Fork - LGPL
41648  * <script type="text/javascript">
41649  */
41650  
41651
41652 /**
41653  * @class Roo.form.NumberField
41654  * @extends Roo.form.TextField
41655  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41656  * @constructor
41657  * Creates a new NumberField
41658  * @param {Object} config Configuration options
41659  */
41660 Roo.form.NumberField = function(config){
41661     Roo.form.NumberField.superclass.constructor.call(this, config);
41662 };
41663
41664 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41665     /**
41666      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41667      */
41668     fieldClass: "x-form-field x-form-num-field",
41669     /**
41670      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41671      */
41672     allowDecimals : true,
41673     /**
41674      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41675      */
41676     decimalSeparator : ".",
41677     /**
41678      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41679      */
41680     decimalPrecision : 2,
41681     /**
41682      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41683      */
41684     allowNegative : true,
41685     /**
41686      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41687      */
41688     minValue : Number.NEGATIVE_INFINITY,
41689     /**
41690      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41691      */
41692     maxValue : Number.MAX_VALUE,
41693     /**
41694      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41695      */
41696     minText : "The minimum value for this field is {0}",
41697     /**
41698      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41699      */
41700     maxText : "The maximum value for this field is {0}",
41701     /**
41702      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41703      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41704      */
41705     nanText : "{0} is not a valid number",
41706
41707     // private
41708     initEvents : function(){
41709         Roo.form.NumberField.superclass.initEvents.call(this);
41710         var allowed = "0123456789";
41711         if(this.allowDecimals){
41712             allowed += this.decimalSeparator;
41713         }
41714         if(this.allowNegative){
41715             allowed += "-";
41716         }
41717         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41718         var keyPress = function(e){
41719             var k = e.getKey();
41720             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41721                 return;
41722             }
41723             var c = e.getCharCode();
41724             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41725                 e.stopEvent();
41726             }
41727         };
41728         this.el.on("keypress", keyPress, this);
41729     },
41730
41731     // private
41732     validateValue : function(value){
41733         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41734             return false;
41735         }
41736         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41737              return true;
41738         }
41739         var num = this.parseValue(value);
41740         if(isNaN(num)){
41741             this.markInvalid(String.format(this.nanText, value));
41742             return false;
41743         }
41744         if(num < this.minValue){
41745             this.markInvalid(String.format(this.minText, this.minValue));
41746             return false;
41747         }
41748         if(num > this.maxValue){
41749             this.markInvalid(String.format(this.maxText, this.maxValue));
41750             return false;
41751         }
41752         return true;
41753     },
41754
41755     getValue : function(){
41756         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41757     },
41758
41759     // private
41760     parseValue : function(value){
41761         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41762         return isNaN(value) ? '' : value;
41763     },
41764
41765     // private
41766     fixPrecision : function(value){
41767         var nan = isNaN(value);
41768         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41769             return nan ? '' : value;
41770         }
41771         return parseFloat(value).toFixed(this.decimalPrecision);
41772     },
41773
41774     setValue : function(v){
41775         v = this.fixPrecision(v);
41776         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41777     },
41778
41779     // private
41780     decimalPrecisionFcn : function(v){
41781         return Math.floor(v);
41782     },
41783
41784     beforeBlur : function(){
41785         var v = this.parseValue(this.getRawValue());
41786         if(v){
41787             this.setValue(v);
41788         }
41789     }
41790 });/*
41791  * Based on:
41792  * Ext JS Library 1.1.1
41793  * Copyright(c) 2006-2007, Ext JS, LLC.
41794  *
41795  * Originally Released Under LGPL - original licence link has changed is not relivant.
41796  *
41797  * Fork - LGPL
41798  * <script type="text/javascript">
41799  */
41800  
41801 /**
41802  * @class Roo.form.DateField
41803  * @extends Roo.form.TriggerField
41804  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41805 * @constructor
41806 * Create a new DateField
41807 * @param {Object} config
41808  */
41809 Roo.form.DateField = function(config)
41810 {
41811     Roo.form.DateField.superclass.constructor.call(this, config);
41812     
41813       this.addEvents({
41814          
41815         /**
41816          * @event select
41817          * Fires when a date is selected
41818              * @param {Roo.form.DateField} combo This combo box
41819              * @param {Date} date The date selected
41820              */
41821         'select' : true
41822          
41823     });
41824     
41825     
41826     if(typeof this.minValue == "string") {
41827         this.minValue = this.parseDate(this.minValue);
41828     }
41829     if(typeof this.maxValue == "string") {
41830         this.maxValue = this.parseDate(this.maxValue);
41831     }
41832     this.ddMatch = null;
41833     if(this.disabledDates){
41834         var dd = this.disabledDates;
41835         var re = "(?:";
41836         for(var i = 0; i < dd.length; i++){
41837             re += dd[i];
41838             if(i != dd.length-1) {
41839                 re += "|";
41840             }
41841         }
41842         this.ddMatch = new RegExp(re + ")");
41843     }
41844 };
41845
41846 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41847     /**
41848      * @cfg {String} format
41849      * The default date format string which can be overriden for localization support.  The format must be
41850      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41851      */
41852     format : "m/d/y",
41853     /**
41854      * @cfg {String} altFormats
41855      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41856      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41857      */
41858     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41859     /**
41860      * @cfg {Array} disabledDays
41861      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41862      */
41863     disabledDays : null,
41864     /**
41865      * @cfg {String} disabledDaysText
41866      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41867      */
41868     disabledDaysText : "Disabled",
41869     /**
41870      * @cfg {Array} disabledDates
41871      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41872      * expression so they are very powerful. Some examples:
41873      * <ul>
41874      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41875      * <li>["03/08", "09/16"] would disable those days for every year</li>
41876      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41877      * <li>["03/../2006"] would disable every day in March 2006</li>
41878      * <li>["^03"] would disable every day in every March</li>
41879      * </ul>
41880      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41881      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41882      */
41883     disabledDates : null,
41884     /**
41885      * @cfg {String} disabledDatesText
41886      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41887      */
41888     disabledDatesText : "Disabled",
41889         
41890         
41891         /**
41892      * @cfg {Date/String} zeroValue
41893      * if the date is less that this number, then the field is rendered as empty
41894      * default is 1800
41895      */
41896         zeroValue : '1800-01-01',
41897         
41898         
41899     /**
41900      * @cfg {Date/String} minValue
41901      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41902      * valid format (defaults to null).
41903      */
41904     minValue : null,
41905     /**
41906      * @cfg {Date/String} maxValue
41907      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41908      * valid format (defaults to null).
41909      */
41910     maxValue : null,
41911     /**
41912      * @cfg {String} minText
41913      * The error text to display when the date in the cell is before minValue (defaults to
41914      * 'The date in this field must be after {minValue}').
41915      */
41916     minText : "The date in this field must be equal to or after {0}",
41917     /**
41918      * @cfg {String} maxText
41919      * The error text to display when the date in the cell is after maxValue (defaults to
41920      * 'The date in this field must be before {maxValue}').
41921      */
41922     maxText : "The date in this field must be equal to or before {0}",
41923     /**
41924      * @cfg {String} invalidText
41925      * The error text to display when the date in the field is invalid (defaults to
41926      * '{value} is not a valid date - it must be in the format {format}').
41927      */
41928     invalidText : "{0} is not a valid date - it must be in the format {1}",
41929     /**
41930      * @cfg {String} triggerClass
41931      * An additional CSS class used to style the trigger button.  The trigger will always get the
41932      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41933      * which displays a calendar icon).
41934      */
41935     triggerClass : 'x-form-date-trigger',
41936     
41937
41938     /**
41939      * @cfg {Boolean} useIso
41940      * if enabled, then the date field will use a hidden field to store the 
41941      * real value as iso formated date. default (false)
41942      */ 
41943     useIso : false,
41944     /**
41945      * @cfg {String/Object} autoCreate
41946      * A DomHelper element spec, or true for a default element spec (defaults to
41947      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41948      */ 
41949     // private
41950     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41951     
41952     // private
41953     hiddenField: false,
41954     
41955     onRender : function(ct, position)
41956     {
41957         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41958         if (this.useIso) {
41959             //this.el.dom.removeAttribute('name'); 
41960             Roo.log("Changing name?");
41961             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41962             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41963                     'before', true);
41964             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41965             // prevent input submission
41966             this.hiddenName = this.name;
41967         }
41968             
41969             
41970     },
41971     
41972     // private
41973     validateValue : function(value)
41974     {
41975         value = this.formatDate(value);
41976         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41977             Roo.log('super failed');
41978             return false;
41979         }
41980         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41981              return true;
41982         }
41983         var svalue = value;
41984         value = this.parseDate(value);
41985         if(!value){
41986             Roo.log('parse date failed' + svalue);
41987             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41988             return false;
41989         }
41990         var time = value.getTime();
41991         if(this.minValue && time < this.minValue.getTime()){
41992             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41993             return false;
41994         }
41995         if(this.maxValue && time > this.maxValue.getTime()){
41996             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41997             return false;
41998         }
41999         if(this.disabledDays){
42000             var day = value.getDay();
42001             for(var i = 0; i < this.disabledDays.length; i++) {
42002                 if(day === this.disabledDays[i]){
42003                     this.markInvalid(this.disabledDaysText);
42004                     return false;
42005                 }
42006             }
42007         }
42008         var fvalue = this.formatDate(value);
42009         if(this.ddMatch && this.ddMatch.test(fvalue)){
42010             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42011             return false;
42012         }
42013         return true;
42014     },
42015
42016     // private
42017     // Provides logic to override the default TriggerField.validateBlur which just returns true
42018     validateBlur : function(){
42019         return !this.menu || !this.menu.isVisible();
42020     },
42021     
42022     getName: function()
42023     {
42024         // returns hidden if it's set..
42025         if (!this.rendered) {return ''};
42026         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42027         
42028     },
42029
42030     /**
42031      * Returns the current date value of the date field.
42032      * @return {Date} The date value
42033      */
42034     getValue : function(){
42035         
42036         return  this.hiddenField ?
42037                 this.hiddenField.value :
42038                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42039     },
42040
42041     /**
42042      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42043      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42044      * (the default format used is "m/d/y").
42045      * <br />Usage:
42046      * <pre><code>
42047 //All of these calls set the same date value (May 4, 2006)
42048
42049 //Pass a date object:
42050 var dt = new Date('5/4/06');
42051 dateField.setValue(dt);
42052
42053 //Pass a date string (default format):
42054 dateField.setValue('5/4/06');
42055
42056 //Pass a date string (custom format):
42057 dateField.format = 'Y-m-d';
42058 dateField.setValue('2006-5-4');
42059 </code></pre>
42060      * @param {String/Date} date The date or valid date string
42061      */
42062     setValue : function(date){
42063         if (this.hiddenField) {
42064             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42065         }
42066         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42067         // make sure the value field is always stored as a date..
42068         this.value = this.parseDate(date);
42069         
42070         
42071     },
42072
42073     // private
42074     parseDate : function(value){
42075                 
42076                 if (value instanceof Date) {
42077                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42078                                 return  '';
42079                         }
42080                         return value;
42081                 }
42082                 
42083                 
42084         if(!value || value instanceof Date){
42085             return value;
42086         }
42087         var v = Date.parseDate(value, this.format);
42088          if (!v && this.useIso) {
42089             v = Date.parseDate(value, 'Y-m-d');
42090         }
42091         if(!v && this.altFormats){
42092             if(!this.altFormatsArray){
42093                 this.altFormatsArray = this.altFormats.split("|");
42094             }
42095             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42096                 v = Date.parseDate(value, this.altFormatsArray[i]);
42097             }
42098         }
42099                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42100                         v = '';
42101                 }
42102         return v;
42103     },
42104
42105     // private
42106     formatDate : function(date, fmt){
42107         return (!date || !(date instanceof Date)) ?
42108                date : date.dateFormat(fmt || this.format);
42109     },
42110
42111     // private
42112     menuListeners : {
42113         select: function(m, d){
42114             
42115             this.setValue(d);
42116             this.fireEvent('select', this, d);
42117         },
42118         show : function(){ // retain focus styling
42119             this.onFocus();
42120         },
42121         hide : function(){
42122             this.focus.defer(10, this);
42123             var ml = this.menuListeners;
42124             this.menu.un("select", ml.select,  this);
42125             this.menu.un("show", ml.show,  this);
42126             this.menu.un("hide", ml.hide,  this);
42127         }
42128     },
42129
42130     // private
42131     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42132     onTriggerClick : function(){
42133         if(this.disabled){
42134             return;
42135         }
42136         if(this.menu == null){
42137             this.menu = new Roo.menu.DateMenu();
42138         }
42139         Roo.apply(this.menu.picker,  {
42140             showClear: this.allowBlank,
42141             minDate : this.minValue,
42142             maxDate : this.maxValue,
42143             disabledDatesRE : this.ddMatch,
42144             disabledDatesText : this.disabledDatesText,
42145             disabledDays : this.disabledDays,
42146             disabledDaysText : this.disabledDaysText,
42147             format : this.useIso ? 'Y-m-d' : this.format,
42148             minText : String.format(this.minText, this.formatDate(this.minValue)),
42149             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42150         });
42151         this.menu.on(Roo.apply({}, this.menuListeners, {
42152             scope:this
42153         }));
42154         this.menu.picker.setValue(this.getValue() || new Date());
42155         this.menu.show(this.el, "tl-bl?");
42156     },
42157
42158     beforeBlur : function(){
42159         var v = this.parseDate(this.getRawValue());
42160         if(v){
42161             this.setValue(v);
42162         }
42163     },
42164
42165     /*@
42166      * overide
42167      * 
42168      */
42169     isDirty : function() {
42170         if(this.disabled) {
42171             return false;
42172         }
42173         
42174         if(typeof(this.startValue) === 'undefined'){
42175             return false;
42176         }
42177         
42178         return String(this.getValue()) !== String(this.startValue);
42179         
42180     },
42181     // @overide
42182     cleanLeadingSpace : function(e)
42183     {
42184        return;
42185     }
42186     
42187 });/*
42188  * Based on:
42189  * Ext JS Library 1.1.1
42190  * Copyright(c) 2006-2007, Ext JS, LLC.
42191  *
42192  * Originally Released Under LGPL - original licence link has changed is not relivant.
42193  *
42194  * Fork - LGPL
42195  * <script type="text/javascript">
42196  */
42197  
42198 /**
42199  * @class Roo.form.MonthField
42200  * @extends Roo.form.TriggerField
42201  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42202 * @constructor
42203 * Create a new MonthField
42204 * @param {Object} config
42205  */
42206 Roo.form.MonthField = function(config){
42207     
42208     Roo.form.MonthField.superclass.constructor.call(this, config);
42209     
42210       this.addEvents({
42211          
42212         /**
42213          * @event select
42214          * Fires when a date is selected
42215              * @param {Roo.form.MonthFieeld} combo This combo box
42216              * @param {Date} date The date selected
42217              */
42218         'select' : true
42219          
42220     });
42221     
42222     
42223     if(typeof this.minValue == "string") {
42224         this.minValue = this.parseDate(this.minValue);
42225     }
42226     if(typeof this.maxValue == "string") {
42227         this.maxValue = this.parseDate(this.maxValue);
42228     }
42229     this.ddMatch = null;
42230     if(this.disabledDates){
42231         var dd = this.disabledDates;
42232         var re = "(?:";
42233         for(var i = 0; i < dd.length; i++){
42234             re += dd[i];
42235             if(i != dd.length-1) {
42236                 re += "|";
42237             }
42238         }
42239         this.ddMatch = new RegExp(re + ")");
42240     }
42241 };
42242
42243 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42244     /**
42245      * @cfg {String} format
42246      * The default date format string which can be overriden for localization support.  The format must be
42247      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42248      */
42249     format : "M Y",
42250     /**
42251      * @cfg {String} altFormats
42252      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42253      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42254      */
42255     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42256     /**
42257      * @cfg {Array} disabledDays
42258      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42259      */
42260     disabledDays : [0,1,2,3,4,5,6],
42261     /**
42262      * @cfg {String} disabledDaysText
42263      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42264      */
42265     disabledDaysText : "Disabled",
42266     /**
42267      * @cfg {Array} disabledDates
42268      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42269      * expression so they are very powerful. Some examples:
42270      * <ul>
42271      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42272      * <li>["03/08", "09/16"] would disable those days for every year</li>
42273      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42274      * <li>["03/../2006"] would disable every day in March 2006</li>
42275      * <li>["^03"] would disable every day in every March</li>
42276      * </ul>
42277      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42278      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42279      */
42280     disabledDates : null,
42281     /**
42282      * @cfg {String} disabledDatesText
42283      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42284      */
42285     disabledDatesText : "Disabled",
42286     /**
42287      * @cfg {Date/String} minValue
42288      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42289      * valid format (defaults to null).
42290      */
42291     minValue : null,
42292     /**
42293      * @cfg {Date/String} maxValue
42294      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42295      * valid format (defaults to null).
42296      */
42297     maxValue : null,
42298     /**
42299      * @cfg {String} minText
42300      * The error text to display when the date in the cell is before minValue (defaults to
42301      * 'The date in this field must be after {minValue}').
42302      */
42303     minText : "The date in this field must be equal to or after {0}",
42304     /**
42305      * @cfg {String} maxTextf
42306      * The error text to display when the date in the cell is after maxValue (defaults to
42307      * 'The date in this field must be before {maxValue}').
42308      */
42309     maxText : "The date in this field must be equal to or before {0}",
42310     /**
42311      * @cfg {String} invalidText
42312      * The error text to display when the date in the field is invalid (defaults to
42313      * '{value} is not a valid date - it must be in the format {format}').
42314      */
42315     invalidText : "{0} is not a valid date - it must be in the format {1}",
42316     /**
42317      * @cfg {String} triggerClass
42318      * An additional CSS class used to style the trigger button.  The trigger will always get the
42319      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42320      * which displays a calendar icon).
42321      */
42322     triggerClass : 'x-form-date-trigger',
42323     
42324
42325     /**
42326      * @cfg {Boolean} useIso
42327      * if enabled, then the date field will use a hidden field to store the 
42328      * real value as iso formated date. default (true)
42329      */ 
42330     useIso : true,
42331     /**
42332      * @cfg {String/Object} autoCreate
42333      * A DomHelper element spec, or true for a default element spec (defaults to
42334      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42335      */ 
42336     // private
42337     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42338     
42339     // private
42340     hiddenField: false,
42341     
42342     hideMonthPicker : false,
42343     
42344     onRender : function(ct, position)
42345     {
42346         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42347         if (this.useIso) {
42348             this.el.dom.removeAttribute('name'); 
42349             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42350                     'before', true);
42351             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42352             // prevent input submission
42353             this.hiddenName = this.name;
42354         }
42355             
42356             
42357     },
42358     
42359     // private
42360     validateValue : function(value)
42361     {
42362         value = this.formatDate(value);
42363         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42364             return false;
42365         }
42366         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42367              return true;
42368         }
42369         var svalue = value;
42370         value = this.parseDate(value);
42371         if(!value){
42372             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42373             return false;
42374         }
42375         var time = value.getTime();
42376         if(this.minValue && time < this.minValue.getTime()){
42377             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42378             return false;
42379         }
42380         if(this.maxValue && time > this.maxValue.getTime()){
42381             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42382             return false;
42383         }
42384         /*if(this.disabledDays){
42385             var day = value.getDay();
42386             for(var i = 0; i < this.disabledDays.length; i++) {
42387                 if(day === this.disabledDays[i]){
42388                     this.markInvalid(this.disabledDaysText);
42389                     return false;
42390                 }
42391             }
42392         }
42393         */
42394         var fvalue = this.formatDate(value);
42395         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42396             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42397             return false;
42398         }
42399         */
42400         return true;
42401     },
42402
42403     // private
42404     // Provides logic to override the default TriggerField.validateBlur which just returns true
42405     validateBlur : function(){
42406         return !this.menu || !this.menu.isVisible();
42407     },
42408
42409     /**
42410      * Returns the current date value of the date field.
42411      * @return {Date} The date value
42412      */
42413     getValue : function(){
42414         
42415         
42416         
42417         return  this.hiddenField ?
42418                 this.hiddenField.value :
42419                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42420     },
42421
42422     /**
42423      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42424      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42425      * (the default format used is "m/d/y").
42426      * <br />Usage:
42427      * <pre><code>
42428 //All of these calls set the same date value (May 4, 2006)
42429
42430 //Pass a date object:
42431 var dt = new Date('5/4/06');
42432 monthField.setValue(dt);
42433
42434 //Pass a date string (default format):
42435 monthField.setValue('5/4/06');
42436
42437 //Pass a date string (custom format):
42438 monthField.format = 'Y-m-d';
42439 monthField.setValue('2006-5-4');
42440 </code></pre>
42441      * @param {String/Date} date The date or valid date string
42442      */
42443     setValue : function(date){
42444         Roo.log('month setValue' + date);
42445         // can only be first of month..
42446         
42447         var val = this.parseDate(date);
42448         
42449         if (this.hiddenField) {
42450             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42451         }
42452         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42453         this.value = this.parseDate(date);
42454     },
42455
42456     // private
42457     parseDate : function(value){
42458         if(!value || value instanceof Date){
42459             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42460             return value;
42461         }
42462         var v = Date.parseDate(value, this.format);
42463         if (!v && this.useIso) {
42464             v = Date.parseDate(value, 'Y-m-d');
42465         }
42466         if (v) {
42467             // 
42468             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42469         }
42470         
42471         
42472         if(!v && this.altFormats){
42473             if(!this.altFormatsArray){
42474                 this.altFormatsArray = this.altFormats.split("|");
42475             }
42476             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42477                 v = Date.parseDate(value, this.altFormatsArray[i]);
42478             }
42479         }
42480         return v;
42481     },
42482
42483     // private
42484     formatDate : function(date, fmt){
42485         return (!date || !(date instanceof Date)) ?
42486                date : date.dateFormat(fmt || this.format);
42487     },
42488
42489     // private
42490     menuListeners : {
42491         select: function(m, d){
42492             this.setValue(d);
42493             this.fireEvent('select', this, d);
42494         },
42495         show : function(){ // retain focus styling
42496             this.onFocus();
42497         },
42498         hide : function(){
42499             this.focus.defer(10, this);
42500             var ml = this.menuListeners;
42501             this.menu.un("select", ml.select,  this);
42502             this.menu.un("show", ml.show,  this);
42503             this.menu.un("hide", ml.hide,  this);
42504         }
42505     },
42506     // private
42507     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42508     onTriggerClick : function(){
42509         if(this.disabled){
42510             return;
42511         }
42512         if(this.menu == null){
42513             this.menu = new Roo.menu.DateMenu();
42514            
42515         }
42516         
42517         Roo.apply(this.menu.picker,  {
42518             
42519             showClear: this.allowBlank,
42520             minDate : this.minValue,
42521             maxDate : this.maxValue,
42522             disabledDatesRE : this.ddMatch,
42523             disabledDatesText : this.disabledDatesText,
42524             
42525             format : this.useIso ? 'Y-m-d' : this.format,
42526             minText : String.format(this.minText, this.formatDate(this.minValue)),
42527             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42528             
42529         });
42530          this.menu.on(Roo.apply({}, this.menuListeners, {
42531             scope:this
42532         }));
42533        
42534         
42535         var m = this.menu;
42536         var p = m.picker;
42537         
42538         // hide month picker get's called when we called by 'before hide';
42539         
42540         var ignorehide = true;
42541         p.hideMonthPicker  = function(disableAnim){
42542             if (ignorehide) {
42543                 return;
42544             }
42545              if(this.monthPicker){
42546                 Roo.log("hideMonthPicker called");
42547                 if(disableAnim === true){
42548                     this.monthPicker.hide();
42549                 }else{
42550                     this.monthPicker.slideOut('t', {duration:.2});
42551                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42552                     p.fireEvent("select", this, this.value);
42553                     m.hide();
42554                 }
42555             }
42556         }
42557         
42558         Roo.log('picker set value');
42559         Roo.log(this.getValue());
42560         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42561         m.show(this.el, 'tl-bl?');
42562         ignorehide  = false;
42563         // this will trigger hideMonthPicker..
42564         
42565         
42566         // hidden the day picker
42567         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42568         
42569         
42570         
42571       
42572         
42573         p.showMonthPicker.defer(100, p);
42574     
42575         
42576        
42577     },
42578
42579     beforeBlur : function(){
42580         var v = this.parseDate(this.getRawValue());
42581         if(v){
42582             this.setValue(v);
42583         }
42584     }
42585
42586     /** @cfg {Boolean} grow @hide */
42587     /** @cfg {Number} growMin @hide */
42588     /** @cfg {Number} growMax @hide */
42589     /**
42590      * @hide
42591      * @method autoSize
42592      */
42593 });/*
42594  * Based on:
42595  * Ext JS Library 1.1.1
42596  * Copyright(c) 2006-2007, Ext JS, LLC.
42597  *
42598  * Originally Released Under LGPL - original licence link has changed is not relivant.
42599  *
42600  * Fork - LGPL
42601  * <script type="text/javascript">
42602  */
42603  
42604
42605 /**
42606  * @class Roo.form.ComboBox
42607  * @extends Roo.form.TriggerField
42608  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42609  * @constructor
42610  * Create a new ComboBox.
42611  * @param {Object} config Configuration options
42612  */
42613 Roo.form.ComboBox = function(config){
42614     Roo.form.ComboBox.superclass.constructor.call(this, config);
42615     this.addEvents({
42616         /**
42617          * @event expand
42618          * Fires when the dropdown list is expanded
42619              * @param {Roo.form.ComboBox} combo This combo box
42620              */
42621         'expand' : true,
42622         /**
42623          * @event collapse
42624          * Fires when the dropdown list is collapsed
42625              * @param {Roo.form.ComboBox} combo This combo box
42626              */
42627         'collapse' : true,
42628         /**
42629          * @event beforeselect
42630          * Fires before a list item is selected. Return false to cancel the selection.
42631              * @param {Roo.form.ComboBox} combo This combo box
42632              * @param {Roo.data.Record} record The data record returned from the underlying store
42633              * @param {Number} index The index of the selected item in the dropdown list
42634              */
42635         'beforeselect' : true,
42636         /**
42637          * @event select
42638          * Fires when a list item is selected
42639              * @param {Roo.form.ComboBox} combo This combo box
42640              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42641              * @param {Number} index The index of the selected item in the dropdown list
42642              */
42643         'select' : true,
42644         /**
42645          * @event beforequery
42646          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42647          * The event object passed has these properties:
42648              * @param {Roo.form.ComboBox} combo This combo box
42649              * @param {String} query The query
42650              * @param {Boolean} forceAll true to force "all" query
42651              * @param {Boolean} cancel true to cancel the query
42652              * @param {Object} e The query event object
42653              */
42654         'beforequery': true,
42655          /**
42656          * @event add
42657          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42658              * @param {Roo.form.ComboBox} combo This combo box
42659              */
42660         'add' : true,
42661         /**
42662          * @event edit
42663          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42664              * @param {Roo.form.ComboBox} combo This combo box
42665              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42666              */
42667         'edit' : true
42668         
42669         
42670     });
42671     if(this.transform){
42672         this.allowDomMove = false;
42673         var s = Roo.getDom(this.transform);
42674         if(!this.hiddenName){
42675             this.hiddenName = s.name;
42676         }
42677         if(!this.store){
42678             this.mode = 'local';
42679             var d = [], opts = s.options;
42680             for(var i = 0, len = opts.length;i < len; i++){
42681                 var o = opts[i];
42682                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42683                 if(o.selected) {
42684                     this.value = value;
42685                 }
42686                 d.push([value, o.text]);
42687             }
42688             this.store = new Roo.data.SimpleStore({
42689                 'id': 0,
42690                 fields: ['value', 'text'],
42691                 data : d
42692             });
42693             this.valueField = 'value';
42694             this.displayField = 'text';
42695         }
42696         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42697         if(!this.lazyRender){
42698             this.target = true;
42699             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42700             s.parentNode.removeChild(s); // remove it
42701             this.render(this.el.parentNode);
42702         }else{
42703             s.parentNode.removeChild(s); // remove it
42704         }
42705
42706     }
42707     if (this.store) {
42708         this.store = Roo.factory(this.store, Roo.data);
42709     }
42710     
42711     this.selectedIndex = -1;
42712     if(this.mode == 'local'){
42713         if(config.queryDelay === undefined){
42714             this.queryDelay = 10;
42715         }
42716         if(config.minChars === undefined){
42717             this.minChars = 0;
42718         }
42719     }
42720 };
42721
42722 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42723     /**
42724      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42725      */
42726     /**
42727      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42728      * rendering into an Roo.Editor, defaults to false)
42729      */
42730     /**
42731      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42732      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42733      */
42734     /**
42735      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42736      */
42737     /**
42738      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42739      * the dropdown list (defaults to undefined, with no header element)
42740      */
42741
42742      /**
42743      * @cfg {String/Roo.Template} tpl The template to use to render the output
42744      */
42745      
42746     // private
42747     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42748     /**
42749      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42750      */
42751     listWidth: undefined,
42752     /**
42753      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42754      * mode = 'remote' or 'text' if mode = 'local')
42755      */
42756     displayField: undefined,
42757     /**
42758      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42759      * mode = 'remote' or 'value' if mode = 'local'). 
42760      * Note: use of a valueField requires the user make a selection
42761      * in order for a value to be mapped.
42762      */
42763     valueField: undefined,
42764     
42765     
42766     /**
42767      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42768      * field's data value (defaults to the underlying DOM element's name)
42769      */
42770     hiddenName: undefined,
42771     /**
42772      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42773      */
42774     listClass: '',
42775     /**
42776      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42777      */
42778     selectedClass: 'x-combo-selected',
42779     /**
42780      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42781      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42782      * which displays a downward arrow icon).
42783      */
42784     triggerClass : 'x-form-arrow-trigger',
42785     /**
42786      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42787      */
42788     shadow:'sides',
42789     /**
42790      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42791      * anchor positions (defaults to 'tl-bl')
42792      */
42793     listAlign: 'tl-bl?',
42794     /**
42795      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42796      */
42797     maxHeight: 300,
42798     /**
42799      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42800      * query specified by the allQuery config option (defaults to 'query')
42801      */
42802     triggerAction: 'query',
42803     /**
42804      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42805      * (defaults to 4, does not apply if editable = false)
42806      */
42807     minChars : 4,
42808     /**
42809      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42810      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42811      */
42812     typeAhead: false,
42813     /**
42814      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42815      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42816      */
42817     queryDelay: 500,
42818     /**
42819      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42820      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42821      */
42822     pageSize: 0,
42823     /**
42824      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42825      * when editable = true (defaults to false)
42826      */
42827     selectOnFocus:false,
42828     /**
42829      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42830      */
42831     queryParam: 'query',
42832     /**
42833      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42834      * when mode = 'remote' (defaults to 'Loading...')
42835      */
42836     loadingText: 'Loading...',
42837     /**
42838      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42839      */
42840     resizable: false,
42841     /**
42842      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42843      */
42844     handleHeight : 8,
42845     /**
42846      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42847      * traditional select (defaults to true)
42848      */
42849     editable: true,
42850     /**
42851      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42852      */
42853     allQuery: '',
42854     /**
42855      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42856      */
42857     mode: 'remote',
42858     /**
42859      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42860      * listWidth has a higher value)
42861      */
42862     minListWidth : 70,
42863     /**
42864      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42865      * allow the user to set arbitrary text into the field (defaults to false)
42866      */
42867     forceSelection:false,
42868     /**
42869      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42870      * if typeAhead = true (defaults to 250)
42871      */
42872     typeAheadDelay : 250,
42873     /**
42874      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42875      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42876      */
42877     valueNotFoundText : undefined,
42878     /**
42879      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42880      */
42881     blockFocus : false,
42882     
42883     /**
42884      * @cfg {Boolean} disableClear Disable showing of clear button.
42885      */
42886     disableClear : false,
42887     /**
42888      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42889      */
42890     alwaysQuery : false,
42891     
42892     //private
42893     addicon : false,
42894     editicon: false,
42895     
42896     // element that contains real text value.. (when hidden is used..)
42897      
42898     // private
42899     onRender : function(ct, position)
42900     {
42901         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42902         
42903         if(this.hiddenName){
42904             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42905                     'before', true);
42906             this.hiddenField.value =
42907                 this.hiddenValue !== undefined ? this.hiddenValue :
42908                 this.value !== undefined ? this.value : '';
42909
42910             // prevent input submission
42911             this.el.dom.removeAttribute('name');
42912              
42913              
42914         }
42915         
42916         if(Roo.isGecko){
42917             this.el.dom.setAttribute('autocomplete', 'off');
42918         }
42919
42920         var cls = 'x-combo-list';
42921
42922         this.list = new Roo.Layer({
42923             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42924         });
42925
42926         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42927         this.list.setWidth(lw);
42928         this.list.swallowEvent('mousewheel');
42929         this.assetHeight = 0;
42930
42931         if(this.title){
42932             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42933             this.assetHeight += this.header.getHeight();
42934         }
42935
42936         this.innerList = this.list.createChild({cls:cls+'-inner'});
42937         this.innerList.on('mouseover', this.onViewOver, this);
42938         this.innerList.on('mousemove', this.onViewMove, this);
42939         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42940         
42941         if(this.allowBlank && !this.pageSize && !this.disableClear){
42942             this.footer = this.list.createChild({cls:cls+'-ft'});
42943             this.pageTb = new Roo.Toolbar(this.footer);
42944            
42945         }
42946         if(this.pageSize){
42947             this.footer = this.list.createChild({cls:cls+'-ft'});
42948             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42949                     {pageSize: this.pageSize});
42950             
42951         }
42952         
42953         if (this.pageTb && this.allowBlank && !this.disableClear) {
42954             var _this = this;
42955             this.pageTb.add(new Roo.Toolbar.Fill(), {
42956                 cls: 'x-btn-icon x-btn-clear',
42957                 text: '&#160;',
42958                 handler: function()
42959                 {
42960                     _this.collapse();
42961                     _this.clearValue();
42962                     _this.onSelect(false, -1);
42963                 }
42964             });
42965         }
42966         if (this.footer) {
42967             this.assetHeight += this.footer.getHeight();
42968         }
42969         
42970
42971         if(!this.tpl){
42972             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42973         }
42974
42975         this.view = new Roo.View(this.innerList, this.tpl, {
42976             singleSelect:true,
42977             store: this.store,
42978             selectedClass: this.selectedClass
42979         });
42980
42981         this.view.on('click', this.onViewClick, this);
42982
42983         this.store.on('beforeload', this.onBeforeLoad, this);
42984         this.store.on('load', this.onLoad, this);
42985         this.store.on('loadexception', this.onLoadException, this);
42986
42987         if(this.resizable){
42988             this.resizer = new Roo.Resizable(this.list,  {
42989                pinned:true, handles:'se'
42990             });
42991             this.resizer.on('resize', function(r, w, h){
42992                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42993                 this.listWidth = w;
42994                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42995                 this.restrictHeight();
42996             }, this);
42997             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42998         }
42999         if(!this.editable){
43000             this.editable = true;
43001             this.setEditable(false);
43002         }  
43003         
43004         
43005         if (typeof(this.events.add.listeners) != 'undefined') {
43006             
43007             this.addicon = this.wrap.createChild(
43008                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43009        
43010             this.addicon.on('click', function(e) {
43011                 this.fireEvent('add', this);
43012             }, this);
43013         }
43014         if (typeof(this.events.edit.listeners) != 'undefined') {
43015             
43016             this.editicon = this.wrap.createChild(
43017                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43018             if (this.addicon) {
43019                 this.editicon.setStyle('margin-left', '40px');
43020             }
43021             this.editicon.on('click', function(e) {
43022                 
43023                 // we fire even  if inothing is selected..
43024                 this.fireEvent('edit', this, this.lastData );
43025                 
43026             }, this);
43027         }
43028         
43029         
43030         
43031     },
43032
43033     // private
43034     initEvents : function(){
43035         Roo.form.ComboBox.superclass.initEvents.call(this);
43036
43037         this.keyNav = new Roo.KeyNav(this.el, {
43038             "up" : function(e){
43039                 this.inKeyMode = true;
43040                 this.selectPrev();
43041             },
43042
43043             "down" : function(e){
43044                 if(!this.isExpanded()){
43045                     this.onTriggerClick();
43046                 }else{
43047                     this.inKeyMode = true;
43048                     this.selectNext();
43049                 }
43050             },
43051
43052             "enter" : function(e){
43053                 this.onViewClick();
43054                 //return true;
43055             },
43056
43057             "esc" : function(e){
43058                 this.collapse();
43059             },
43060
43061             "tab" : function(e){
43062                 this.onViewClick(false);
43063                 this.fireEvent("specialkey", this, e);
43064                 return true;
43065             },
43066
43067             scope : this,
43068
43069             doRelay : function(foo, bar, hname){
43070                 if(hname == 'down' || this.scope.isExpanded()){
43071                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43072                 }
43073                 return true;
43074             },
43075
43076             forceKeyDown: true
43077         });
43078         this.queryDelay = Math.max(this.queryDelay || 10,
43079                 this.mode == 'local' ? 10 : 250);
43080         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43081         if(this.typeAhead){
43082             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43083         }
43084         if(this.editable !== false){
43085             this.el.on("keyup", this.onKeyUp, this);
43086         }
43087         if(this.forceSelection){
43088             this.on('blur', this.doForce, this);
43089         }
43090     },
43091
43092     onDestroy : function(){
43093         if(this.view){
43094             this.view.setStore(null);
43095             this.view.el.removeAllListeners();
43096             this.view.el.remove();
43097             this.view.purgeListeners();
43098         }
43099         if(this.list){
43100             this.list.destroy();
43101         }
43102         if(this.store){
43103             this.store.un('beforeload', this.onBeforeLoad, this);
43104             this.store.un('load', this.onLoad, this);
43105             this.store.un('loadexception', this.onLoadException, this);
43106         }
43107         Roo.form.ComboBox.superclass.onDestroy.call(this);
43108     },
43109
43110     // private
43111     fireKey : function(e){
43112         if(e.isNavKeyPress() && !this.list.isVisible()){
43113             this.fireEvent("specialkey", this, e);
43114         }
43115     },
43116
43117     // private
43118     onResize: function(w, h){
43119         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43120         
43121         if(typeof w != 'number'){
43122             // we do not handle it!?!?
43123             return;
43124         }
43125         var tw = this.trigger.getWidth();
43126         tw += this.addicon ? this.addicon.getWidth() : 0;
43127         tw += this.editicon ? this.editicon.getWidth() : 0;
43128         var x = w - tw;
43129         this.el.setWidth( this.adjustWidth('input', x));
43130             
43131         this.trigger.setStyle('left', x+'px');
43132         
43133         if(this.list && this.listWidth === undefined){
43134             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43135             this.list.setWidth(lw);
43136             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43137         }
43138         
43139     
43140         
43141     },
43142
43143     /**
43144      * Allow or prevent the user from directly editing the field text.  If false is passed,
43145      * the user will only be able to select from the items defined in the dropdown list.  This method
43146      * is the runtime equivalent of setting the 'editable' config option at config time.
43147      * @param {Boolean} value True to allow the user to directly edit the field text
43148      */
43149     setEditable : function(value){
43150         if(value == this.editable){
43151             return;
43152         }
43153         this.editable = value;
43154         if(!value){
43155             this.el.dom.setAttribute('readOnly', true);
43156             this.el.on('mousedown', this.onTriggerClick,  this);
43157             this.el.addClass('x-combo-noedit');
43158         }else{
43159             this.el.dom.setAttribute('readOnly', false);
43160             this.el.un('mousedown', this.onTriggerClick,  this);
43161             this.el.removeClass('x-combo-noedit');
43162         }
43163     },
43164
43165     // private
43166     onBeforeLoad : function(){
43167         if(!this.hasFocus){
43168             return;
43169         }
43170         this.innerList.update(this.loadingText ?
43171                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43172         this.restrictHeight();
43173         this.selectedIndex = -1;
43174     },
43175
43176     // private
43177     onLoad : function(){
43178         if(!this.hasFocus){
43179             return;
43180         }
43181         if(this.store.getCount() > 0){
43182             this.expand();
43183             this.restrictHeight();
43184             if(this.lastQuery == this.allQuery){
43185                 if(this.editable){
43186                     this.el.dom.select();
43187                 }
43188                 if(!this.selectByValue(this.value, true)){
43189                     this.select(0, true);
43190                 }
43191             }else{
43192                 this.selectNext();
43193                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43194                     this.taTask.delay(this.typeAheadDelay);
43195                 }
43196             }
43197         }else{
43198             this.onEmptyResults();
43199         }
43200         //this.el.focus();
43201     },
43202     // private
43203     onLoadException : function()
43204     {
43205         this.collapse();
43206         Roo.log(this.store.reader.jsonData);
43207         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43208             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43209         }
43210         
43211         
43212     },
43213     // private
43214     onTypeAhead : function(){
43215         if(this.store.getCount() > 0){
43216             var r = this.store.getAt(0);
43217             var newValue = r.data[this.displayField];
43218             var len = newValue.length;
43219             var selStart = this.getRawValue().length;
43220             if(selStart != len){
43221                 this.setRawValue(newValue);
43222                 this.selectText(selStart, newValue.length);
43223             }
43224         }
43225     },
43226
43227     // private
43228     onSelect : function(record, index){
43229         if(this.fireEvent('beforeselect', this, record, index) !== false){
43230             this.setFromData(index > -1 ? record.data : false);
43231             this.collapse();
43232             this.fireEvent('select', this, record, index);
43233         }
43234     },
43235
43236     /**
43237      * Returns the currently selected field value or empty string if no value is set.
43238      * @return {String} value The selected value
43239      */
43240     getValue : function(){
43241         if(this.valueField){
43242             return typeof this.value != 'undefined' ? this.value : '';
43243         }
43244         return Roo.form.ComboBox.superclass.getValue.call(this);
43245     },
43246
43247     /**
43248      * Clears any text/value currently set in the field
43249      */
43250     clearValue : function(){
43251         if(this.hiddenField){
43252             this.hiddenField.value = '';
43253         }
43254         this.value = '';
43255         this.setRawValue('');
43256         this.lastSelectionText = '';
43257         
43258     },
43259
43260     /**
43261      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43262      * will be displayed in the field.  If the value does not match the data value of an existing item,
43263      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43264      * Otherwise the field will be blank (although the value will still be set).
43265      * @param {String} value The value to match
43266      */
43267     setValue : function(v){
43268         var text = v;
43269         if(this.valueField){
43270             var r = this.findRecord(this.valueField, v);
43271             if(r){
43272                 text = r.data[this.displayField];
43273             }else if(this.valueNotFoundText !== undefined){
43274                 text = this.valueNotFoundText;
43275             }
43276         }
43277         this.lastSelectionText = text;
43278         if(this.hiddenField){
43279             this.hiddenField.value = v;
43280         }
43281         Roo.form.ComboBox.superclass.setValue.call(this, text);
43282         this.value = v;
43283     },
43284     /**
43285      * @property {Object} the last set data for the element
43286      */
43287     
43288     lastData : false,
43289     /**
43290      * Sets the value of the field based on a object which is related to the record format for the store.
43291      * @param {Object} value the value to set as. or false on reset?
43292      */
43293     setFromData : function(o){
43294         var dv = ''; // display value
43295         var vv = ''; // value value..
43296         this.lastData = o;
43297         if (this.displayField) {
43298             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43299         } else {
43300             // this is an error condition!!!
43301             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43302         }
43303         
43304         if(this.valueField){
43305             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43306         }
43307         if(this.hiddenField){
43308             this.hiddenField.value = vv;
43309             
43310             this.lastSelectionText = dv;
43311             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43312             this.value = vv;
43313             return;
43314         }
43315         // no hidden field.. - we store the value in 'value', but still display
43316         // display field!!!!
43317         this.lastSelectionText = dv;
43318         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43319         this.value = vv;
43320         
43321         
43322     },
43323     // private
43324     reset : function(){
43325         // overridden so that last data is reset..
43326         this.setValue(this.resetValue);
43327         this.originalValue = this.getValue();
43328         this.clearInvalid();
43329         this.lastData = false;
43330         if (this.view) {
43331             this.view.clearSelections();
43332         }
43333     },
43334     // private
43335     findRecord : function(prop, value){
43336         var record;
43337         if(this.store.getCount() > 0){
43338             this.store.each(function(r){
43339                 if(r.data[prop] == value){
43340                     record = r;
43341                     return false;
43342                 }
43343                 return true;
43344             });
43345         }
43346         return record;
43347     },
43348     
43349     getName: function()
43350     {
43351         // returns hidden if it's set..
43352         if (!this.rendered) {return ''};
43353         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43354         
43355     },
43356     // private
43357     onViewMove : function(e, t){
43358         this.inKeyMode = false;
43359     },
43360
43361     // private
43362     onViewOver : function(e, t){
43363         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43364             return;
43365         }
43366         var item = this.view.findItemFromChild(t);
43367         if(item){
43368             var index = this.view.indexOf(item);
43369             this.select(index, false);
43370         }
43371     },
43372
43373     // private
43374     onViewClick : function(doFocus)
43375     {
43376         var index = this.view.getSelectedIndexes()[0];
43377         var r = this.store.getAt(index);
43378         if(r){
43379             this.onSelect(r, index);
43380         }
43381         if(doFocus !== false && !this.blockFocus){
43382             this.el.focus();
43383         }
43384     },
43385
43386     // private
43387     restrictHeight : function(){
43388         this.innerList.dom.style.height = '';
43389         var inner = this.innerList.dom;
43390         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43391         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43392         this.list.beginUpdate();
43393         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43394         this.list.alignTo(this.el, this.listAlign);
43395         this.list.endUpdate();
43396     },
43397
43398     // private
43399     onEmptyResults : function(){
43400         this.collapse();
43401     },
43402
43403     /**
43404      * Returns true if the dropdown list is expanded, else false.
43405      */
43406     isExpanded : function(){
43407         return this.list.isVisible();
43408     },
43409
43410     /**
43411      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43412      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43413      * @param {String} value The data value of the item to select
43414      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43415      * selected item if it is not currently in view (defaults to true)
43416      * @return {Boolean} True if the value matched an item in the list, else false
43417      */
43418     selectByValue : function(v, scrollIntoView){
43419         if(v !== undefined && v !== null){
43420             var r = this.findRecord(this.valueField || this.displayField, v);
43421             if(r){
43422                 this.select(this.store.indexOf(r), scrollIntoView);
43423                 return true;
43424             }
43425         }
43426         return false;
43427     },
43428
43429     /**
43430      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43431      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43432      * @param {Number} index The zero-based index of the list item to select
43433      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43434      * selected item if it is not currently in view (defaults to true)
43435      */
43436     select : function(index, scrollIntoView){
43437         this.selectedIndex = index;
43438         this.view.select(index);
43439         if(scrollIntoView !== false){
43440             var el = this.view.getNode(index);
43441             if(el){
43442                 this.innerList.scrollChildIntoView(el, false);
43443             }
43444         }
43445     },
43446
43447     // private
43448     selectNext : function(){
43449         var ct = this.store.getCount();
43450         if(ct > 0){
43451             if(this.selectedIndex == -1){
43452                 this.select(0);
43453             }else if(this.selectedIndex < ct-1){
43454                 this.select(this.selectedIndex+1);
43455             }
43456         }
43457     },
43458
43459     // private
43460     selectPrev : function(){
43461         var ct = this.store.getCount();
43462         if(ct > 0){
43463             if(this.selectedIndex == -1){
43464                 this.select(0);
43465             }else if(this.selectedIndex != 0){
43466                 this.select(this.selectedIndex-1);
43467             }
43468         }
43469     },
43470
43471     // private
43472     onKeyUp : function(e){
43473         if(this.editable !== false && !e.isSpecialKey()){
43474             this.lastKey = e.getKey();
43475             this.dqTask.delay(this.queryDelay);
43476         }
43477     },
43478
43479     // private
43480     validateBlur : function(){
43481         return !this.list || !this.list.isVisible();   
43482     },
43483
43484     // private
43485     initQuery : function(){
43486         this.doQuery(this.getRawValue());
43487     },
43488
43489     // private
43490     doForce : function(){
43491         if(this.el.dom.value.length > 0){
43492             this.el.dom.value =
43493                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43494              
43495         }
43496     },
43497
43498     /**
43499      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43500      * query allowing the query action to be canceled if needed.
43501      * @param {String} query The SQL query to execute
43502      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43503      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43504      * saved in the current store (defaults to false)
43505      */
43506     doQuery : function(q, forceAll){
43507         if(q === undefined || q === null){
43508             q = '';
43509         }
43510         var qe = {
43511             query: q,
43512             forceAll: forceAll,
43513             combo: this,
43514             cancel:false
43515         };
43516         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43517             return false;
43518         }
43519         q = qe.query;
43520         forceAll = qe.forceAll;
43521         if(forceAll === true || (q.length >= this.minChars)){
43522             if(this.lastQuery != q || this.alwaysQuery){
43523                 this.lastQuery = q;
43524                 if(this.mode == 'local'){
43525                     this.selectedIndex = -1;
43526                     if(forceAll){
43527                         this.store.clearFilter();
43528                     }else{
43529                         this.store.filter(this.displayField, q);
43530                     }
43531                     this.onLoad();
43532                 }else{
43533                     this.store.baseParams[this.queryParam] = q;
43534                     this.store.load({
43535                         params: this.getParams(q)
43536                     });
43537                     this.expand();
43538                 }
43539             }else{
43540                 this.selectedIndex = -1;
43541                 this.onLoad();   
43542             }
43543         }
43544     },
43545
43546     // private
43547     getParams : function(q){
43548         var p = {};
43549         //p[this.queryParam] = q;
43550         if(this.pageSize){
43551             p.start = 0;
43552             p.limit = this.pageSize;
43553         }
43554         return p;
43555     },
43556
43557     /**
43558      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43559      */
43560     collapse : function(){
43561         if(!this.isExpanded()){
43562             return;
43563         }
43564         this.list.hide();
43565         Roo.get(document).un('mousedown', this.collapseIf, this);
43566         Roo.get(document).un('mousewheel', this.collapseIf, this);
43567         if (!this.editable) {
43568             Roo.get(document).un('keydown', this.listKeyPress, this);
43569         }
43570         this.fireEvent('collapse', this);
43571     },
43572
43573     // private
43574     collapseIf : function(e){
43575         if(!e.within(this.wrap) && !e.within(this.list)){
43576             this.collapse();
43577         }
43578     },
43579
43580     /**
43581      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43582      */
43583     expand : function(){
43584         if(this.isExpanded() || !this.hasFocus){
43585             return;
43586         }
43587         this.list.alignTo(this.el, this.listAlign);
43588         this.list.show();
43589         Roo.get(document).on('mousedown', this.collapseIf, this);
43590         Roo.get(document).on('mousewheel', this.collapseIf, this);
43591         if (!this.editable) {
43592             Roo.get(document).on('keydown', this.listKeyPress, this);
43593         }
43594         
43595         this.fireEvent('expand', this);
43596     },
43597
43598     // private
43599     // Implements the default empty TriggerField.onTriggerClick function
43600     onTriggerClick : function(){
43601         if(this.disabled){
43602             return;
43603         }
43604         if(this.isExpanded()){
43605             this.collapse();
43606             if (!this.blockFocus) {
43607                 this.el.focus();
43608             }
43609             
43610         }else {
43611             this.hasFocus = true;
43612             if(this.triggerAction == 'all') {
43613                 this.doQuery(this.allQuery, true);
43614             } else {
43615                 this.doQuery(this.getRawValue());
43616             }
43617             if (!this.blockFocus) {
43618                 this.el.focus();
43619             }
43620         }
43621     },
43622     listKeyPress : function(e)
43623     {
43624         //Roo.log('listkeypress');
43625         // scroll to first matching element based on key pres..
43626         if (e.isSpecialKey()) {
43627             return false;
43628         }
43629         var k = String.fromCharCode(e.getKey()).toUpperCase();
43630         //Roo.log(k);
43631         var match  = false;
43632         var csel = this.view.getSelectedNodes();
43633         var cselitem = false;
43634         if (csel.length) {
43635             var ix = this.view.indexOf(csel[0]);
43636             cselitem  = this.store.getAt(ix);
43637             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43638                 cselitem = false;
43639             }
43640             
43641         }
43642         
43643         this.store.each(function(v) { 
43644             if (cselitem) {
43645                 // start at existing selection.
43646                 if (cselitem.id == v.id) {
43647                     cselitem = false;
43648                 }
43649                 return;
43650             }
43651                 
43652             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43653                 match = this.store.indexOf(v);
43654                 return false;
43655             }
43656         }, this);
43657         
43658         if (match === false) {
43659             return true; // no more action?
43660         }
43661         // scroll to?
43662         this.view.select(match);
43663         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43664         sn.scrollIntoView(sn.dom.parentNode, false);
43665     } 
43666
43667     /** 
43668     * @cfg {Boolean} grow 
43669     * @hide 
43670     */
43671     /** 
43672     * @cfg {Number} growMin 
43673     * @hide 
43674     */
43675     /** 
43676     * @cfg {Number} growMax 
43677     * @hide 
43678     */
43679     /**
43680      * @hide
43681      * @method autoSize
43682      */
43683 });/*
43684  * Copyright(c) 2010-2012, Roo J Solutions Limited
43685  *
43686  * Licence LGPL
43687  *
43688  */
43689
43690 /**
43691  * @class Roo.form.ComboBoxArray
43692  * @extends Roo.form.TextField
43693  * A facebook style adder... for lists of email / people / countries  etc...
43694  * pick multiple items from a combo box, and shows each one.
43695  *
43696  *  Fred [x]  Brian [x]  [Pick another |v]
43697  *
43698  *
43699  *  For this to work: it needs various extra information
43700  *    - normal combo problay has
43701  *      name, hiddenName
43702  *    + displayField, valueField
43703  *
43704  *    For our purpose...
43705  *
43706  *
43707  *   If we change from 'extends' to wrapping...
43708  *   
43709  *  
43710  *
43711  
43712  
43713  * @constructor
43714  * Create a new ComboBoxArray.
43715  * @param {Object} config Configuration options
43716  */
43717  
43718
43719 Roo.form.ComboBoxArray = function(config)
43720 {
43721     this.addEvents({
43722         /**
43723          * @event beforeremove
43724          * Fires before remove the value from the list
43725              * @param {Roo.form.ComboBoxArray} _self This combo box array
43726              * @param {Roo.form.ComboBoxArray.Item} item removed item
43727              */
43728         'beforeremove' : true,
43729         /**
43730          * @event remove
43731          * Fires when remove the value from the list
43732              * @param {Roo.form.ComboBoxArray} _self This combo box array
43733              * @param {Roo.form.ComboBoxArray.Item} item removed item
43734              */
43735         'remove' : true
43736         
43737         
43738     });
43739     
43740     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43741     
43742     this.items = new Roo.util.MixedCollection(false);
43743     
43744     // construct the child combo...
43745     
43746     
43747     
43748     
43749    
43750     
43751 }
43752
43753  
43754 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43755
43756     /**
43757      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43758      */
43759     
43760     lastData : false,
43761     
43762     // behavies liek a hiddne field
43763     inputType:      'hidden',
43764     /**
43765      * @cfg {Number} width The width of the box that displays the selected element
43766      */ 
43767     width:          300,
43768
43769     
43770     
43771     /**
43772      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43773      */
43774     name : false,
43775     /**
43776      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43777      */
43778     hiddenName : false,
43779       /**
43780      * @cfg {String} seperator    The value seperator normally ',' 
43781      */
43782     seperator : ',',
43783     
43784     // private the array of items that are displayed..
43785     items  : false,
43786     // private - the hidden field el.
43787     hiddenEl : false,
43788     // private - the filed el..
43789     el : false,
43790     
43791     //validateValue : function() { return true; }, // all values are ok!
43792     //onAddClick: function() { },
43793     
43794     onRender : function(ct, position) 
43795     {
43796         
43797         // create the standard hidden element
43798         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43799         
43800         
43801         // give fake names to child combo;
43802         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43803         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43804         
43805         this.combo = Roo.factory(this.combo, Roo.form);
43806         this.combo.onRender(ct, position);
43807         if (typeof(this.combo.width) != 'undefined') {
43808             this.combo.onResize(this.combo.width,0);
43809         }
43810         
43811         this.combo.initEvents();
43812         
43813         // assigned so form know we need to do this..
43814         this.store          = this.combo.store;
43815         this.valueField     = this.combo.valueField;
43816         this.displayField   = this.combo.displayField ;
43817         
43818         
43819         this.combo.wrap.addClass('x-cbarray-grp');
43820         
43821         var cbwrap = this.combo.wrap.createChild(
43822             {tag: 'div', cls: 'x-cbarray-cb'},
43823             this.combo.el.dom
43824         );
43825         
43826              
43827         this.hiddenEl = this.combo.wrap.createChild({
43828             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43829         });
43830         this.el = this.combo.wrap.createChild({
43831             tag: 'input',  type:'hidden' , name: this.name, value : ''
43832         });
43833          //   this.el.dom.removeAttribute("name");
43834         
43835         
43836         this.outerWrap = this.combo.wrap;
43837         this.wrap = cbwrap;
43838         
43839         this.outerWrap.setWidth(this.width);
43840         this.outerWrap.dom.removeChild(this.el.dom);
43841         
43842         this.wrap.dom.appendChild(this.el.dom);
43843         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43844         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43845         
43846         this.combo.trigger.setStyle('position','relative');
43847         this.combo.trigger.setStyle('left', '0px');
43848         this.combo.trigger.setStyle('top', '2px');
43849         
43850         this.combo.el.setStyle('vertical-align', 'text-bottom');
43851         
43852         //this.trigger.setStyle('vertical-align', 'top');
43853         
43854         // this should use the code from combo really... on('add' ....)
43855         if (this.adder) {
43856             
43857         
43858             this.adder = this.outerWrap.createChild(
43859                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43860             var _t = this;
43861             this.adder.on('click', function(e) {
43862                 _t.fireEvent('adderclick', this, e);
43863             }, _t);
43864         }
43865         //var _t = this;
43866         //this.adder.on('click', this.onAddClick, _t);
43867         
43868         
43869         this.combo.on('select', function(cb, rec, ix) {
43870             this.addItem(rec.data);
43871             
43872             cb.setValue('');
43873             cb.el.dom.value = '';
43874             //cb.lastData = rec.data;
43875             // add to list
43876             
43877         }, this);
43878         
43879         
43880     },
43881     
43882     
43883     getName: function()
43884     {
43885         // returns hidden if it's set..
43886         if (!this.rendered) {return ''};
43887         return  this.hiddenName ? this.hiddenName : this.name;
43888         
43889     },
43890     
43891     
43892     onResize: function(w, h){
43893         
43894         return;
43895         // not sure if this is needed..
43896         //this.combo.onResize(w,h);
43897         
43898         if(typeof w != 'number'){
43899             // we do not handle it!?!?
43900             return;
43901         }
43902         var tw = this.combo.trigger.getWidth();
43903         tw += this.addicon ? this.addicon.getWidth() : 0;
43904         tw += this.editicon ? this.editicon.getWidth() : 0;
43905         var x = w - tw;
43906         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43907             
43908         this.combo.trigger.setStyle('left', '0px');
43909         
43910         if(this.list && this.listWidth === undefined){
43911             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43912             this.list.setWidth(lw);
43913             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43914         }
43915         
43916     
43917         
43918     },
43919     
43920     addItem: function(rec)
43921     {
43922         var valueField = this.combo.valueField;
43923         var displayField = this.combo.displayField;
43924         
43925         if (this.items.indexOfKey(rec[valueField]) > -1) {
43926             //console.log("GOT " + rec.data.id);
43927             return;
43928         }
43929         
43930         var x = new Roo.form.ComboBoxArray.Item({
43931             //id : rec[this.idField],
43932             data : rec,
43933             displayField : displayField ,
43934             tipField : displayField ,
43935             cb : this
43936         });
43937         // use the 
43938         this.items.add(rec[valueField],x);
43939         // add it before the element..
43940         this.updateHiddenEl();
43941         x.render(this.outerWrap, this.wrap.dom);
43942         // add the image handler..
43943     },
43944     
43945     updateHiddenEl : function()
43946     {
43947         this.validate();
43948         if (!this.hiddenEl) {
43949             return;
43950         }
43951         var ar = [];
43952         var idField = this.combo.valueField;
43953         
43954         this.items.each(function(f) {
43955             ar.push(f.data[idField]);
43956         });
43957         this.hiddenEl.dom.value = ar.join(this.seperator);
43958         this.validate();
43959     },
43960     
43961     reset : function()
43962     {
43963         this.items.clear();
43964         
43965         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43966            el.remove();
43967         });
43968         
43969         this.el.dom.value = '';
43970         if (this.hiddenEl) {
43971             this.hiddenEl.dom.value = '';
43972         }
43973         
43974     },
43975     getValue: function()
43976     {
43977         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43978     },
43979     setValue: function(v) // not a valid action - must use addItems..
43980     {
43981         
43982         this.reset();
43983          
43984         if (this.store.isLocal && (typeof(v) == 'string')) {
43985             // then we can use the store to find the values..
43986             // comma seperated at present.. this needs to allow JSON based encoding..
43987             this.hiddenEl.value  = v;
43988             var v_ar = [];
43989             Roo.each(v.split(this.seperator), function(k) {
43990                 Roo.log("CHECK " + this.valueField + ',' + k);
43991                 var li = this.store.query(this.valueField, k);
43992                 if (!li.length) {
43993                     return;
43994                 }
43995                 var add = {};
43996                 add[this.valueField] = k;
43997                 add[this.displayField] = li.item(0).data[this.displayField];
43998                 
43999                 this.addItem(add);
44000             }, this) 
44001              
44002         }
44003         if (typeof(v) == 'object' ) {
44004             // then let's assume it's an array of objects..
44005             Roo.each(v, function(l) {
44006                 var add = l;
44007                 if (typeof(l) == 'string') {
44008                     add = {};
44009                     add[this.valueField] = l;
44010                     add[this.displayField] = l
44011                 }
44012                 this.addItem(add);
44013             }, this);
44014              
44015         }
44016         
44017         
44018     },
44019     setFromData: function(v)
44020     {
44021         // this recieves an object, if setValues is called.
44022         this.reset();
44023         this.el.dom.value = v[this.displayField];
44024         this.hiddenEl.dom.value = v[this.valueField];
44025         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44026             return;
44027         }
44028         var kv = v[this.valueField];
44029         var dv = v[this.displayField];
44030         kv = typeof(kv) != 'string' ? '' : kv;
44031         dv = typeof(dv) != 'string' ? '' : dv;
44032         
44033         
44034         var keys = kv.split(this.seperator);
44035         var display = dv.split(this.seperator);
44036         for (var i = 0 ; i < keys.length; i++) {
44037             add = {};
44038             add[this.valueField] = keys[i];
44039             add[this.displayField] = display[i];
44040             this.addItem(add);
44041         }
44042       
44043         
44044     },
44045     
44046     /**
44047      * Validates the combox array value
44048      * @return {Boolean} True if the value is valid, else false
44049      */
44050     validate : function(){
44051         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44052             this.clearInvalid();
44053             return true;
44054         }
44055         return false;
44056     },
44057     
44058     validateValue : function(value){
44059         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44060         
44061     },
44062     
44063     /*@
44064      * overide
44065      * 
44066      */
44067     isDirty : function() {
44068         if(this.disabled) {
44069             return false;
44070         }
44071         
44072         try {
44073             var d = Roo.decode(String(this.originalValue));
44074         } catch (e) {
44075             return String(this.getValue()) !== String(this.originalValue);
44076         }
44077         
44078         var originalValue = [];
44079         
44080         for (var i = 0; i < d.length; i++){
44081             originalValue.push(d[i][this.valueField]);
44082         }
44083         
44084         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44085         
44086     }
44087     
44088 });
44089
44090
44091
44092 /**
44093  * @class Roo.form.ComboBoxArray.Item
44094  * @extends Roo.BoxComponent
44095  * A selected item in the list
44096  *  Fred [x]  Brian [x]  [Pick another |v]
44097  * 
44098  * @constructor
44099  * Create a new item.
44100  * @param {Object} config Configuration options
44101  */
44102  
44103 Roo.form.ComboBoxArray.Item = function(config) {
44104     config.id = Roo.id();
44105     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44106 }
44107
44108 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44109     data : {},
44110     cb: false,
44111     displayField : false,
44112     tipField : false,
44113     
44114     
44115     defaultAutoCreate : {
44116         tag: 'div',
44117         cls: 'x-cbarray-item',
44118         cn : [ 
44119             { tag: 'div' },
44120             {
44121                 tag: 'img',
44122                 width:16,
44123                 height : 16,
44124                 src : Roo.BLANK_IMAGE_URL ,
44125                 align: 'center'
44126             }
44127         ]
44128         
44129     },
44130     
44131  
44132     onRender : function(ct, position)
44133     {
44134         Roo.form.Field.superclass.onRender.call(this, ct, position);
44135         
44136         if(!this.el){
44137             var cfg = this.getAutoCreate();
44138             this.el = ct.createChild(cfg, position);
44139         }
44140         
44141         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44142         
44143         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44144             this.cb.renderer(this.data) :
44145             String.format('{0}',this.data[this.displayField]);
44146         
44147             
44148         this.el.child('div').dom.setAttribute('qtip',
44149                         String.format('{0}',this.data[this.tipField])
44150         );
44151         
44152         this.el.child('img').on('click', this.remove, this);
44153         
44154     },
44155    
44156     remove : function()
44157     {
44158         if(this.cb.disabled){
44159             return;
44160         }
44161         
44162         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44163             this.cb.items.remove(this);
44164             this.el.child('img').un('click', this.remove, this);
44165             this.el.remove();
44166             this.cb.updateHiddenEl();
44167
44168             this.cb.fireEvent('remove', this.cb, this);
44169         }
44170         
44171     }
44172 });/*
44173  * RooJS Library 1.1.1
44174  * Copyright(c) 2008-2011  Alan Knowles
44175  *
44176  * License - LGPL
44177  */
44178  
44179
44180 /**
44181  * @class Roo.form.ComboNested
44182  * @extends Roo.form.ComboBox
44183  * A combobox for that allows selection of nested items in a list,
44184  * eg.
44185  *
44186  *  Book
44187  *    -> red
44188  *    -> green
44189  *  Table
44190  *    -> square
44191  *      ->red
44192  *      ->green
44193  *    -> rectangle
44194  *      ->green
44195  *      
44196  * 
44197  * @constructor
44198  * Create a new ComboNested
44199  * @param {Object} config Configuration options
44200  */
44201 Roo.form.ComboNested = function(config){
44202     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44203     // should verify some data...
44204     // like
44205     // hiddenName = required..
44206     // displayField = required
44207     // valudField == required
44208     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44209     var _t = this;
44210     Roo.each(req, function(e) {
44211         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44212             throw "Roo.form.ComboNested : missing value for: " + e;
44213         }
44214     });
44215      
44216     
44217 };
44218
44219 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44220    
44221     /*
44222      * @config {Number} max Number of columns to show
44223      */
44224     
44225     maxColumns : 3,
44226    
44227     list : null, // the outermost div..
44228     innerLists : null, // the
44229     views : null,
44230     stores : null,
44231     // private
44232     loadingChildren : false,
44233     
44234     onRender : function(ct, position)
44235     {
44236         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44237         
44238         if(this.hiddenName){
44239             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44240                     'before', true);
44241             this.hiddenField.value =
44242                 this.hiddenValue !== undefined ? this.hiddenValue :
44243                 this.value !== undefined ? this.value : '';
44244
44245             // prevent input submission
44246             this.el.dom.removeAttribute('name');
44247              
44248              
44249         }
44250         
44251         if(Roo.isGecko){
44252             this.el.dom.setAttribute('autocomplete', 'off');
44253         }
44254
44255         var cls = 'x-combo-list';
44256
44257         this.list = new Roo.Layer({
44258             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44259         });
44260
44261         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44262         this.list.setWidth(lw);
44263         this.list.swallowEvent('mousewheel');
44264         this.assetHeight = 0;
44265
44266         if(this.title){
44267             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44268             this.assetHeight += this.header.getHeight();
44269         }
44270         this.innerLists = [];
44271         this.views = [];
44272         this.stores = [];
44273         for (var i =0 ; i < this.maxColumns; i++) {
44274             this.onRenderList( cls, i);
44275         }
44276         
44277         // always needs footer, as we are going to have an 'OK' button.
44278         this.footer = this.list.createChild({cls:cls+'-ft'});
44279         this.pageTb = new Roo.Toolbar(this.footer);  
44280         var _this = this;
44281         this.pageTb.add(  {
44282             
44283             text: 'Done',
44284             handler: function()
44285             {
44286                 _this.collapse();
44287             }
44288         });
44289         
44290         if ( this.allowBlank && !this.disableClear) {
44291             
44292             this.pageTb.add(new Roo.Toolbar.Fill(), {
44293                 cls: 'x-btn-icon x-btn-clear',
44294                 text: '&#160;',
44295                 handler: function()
44296                 {
44297                     _this.collapse();
44298                     _this.clearValue();
44299                     _this.onSelect(false, -1);
44300                 }
44301             });
44302         }
44303         if (this.footer) {
44304             this.assetHeight += this.footer.getHeight();
44305         }
44306         
44307     },
44308     onRenderList : function (  cls, i)
44309     {
44310         
44311         var lw = Math.floor(
44312                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44313         );
44314         
44315         this.list.setWidth(lw); // default to '1'
44316
44317         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44318         //il.on('mouseover', this.onViewOver, this, { list:  i });
44319         //il.on('mousemove', this.onViewMove, this, { list:  i });
44320         il.setWidth(lw);
44321         il.setStyle({ 'overflow-x' : 'hidden'});
44322
44323         if(!this.tpl){
44324             this.tpl = new Roo.Template({
44325                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44326                 isEmpty: function (value, allValues) {
44327                     //Roo.log(value);
44328                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44329                     return dl ? 'has-children' : 'no-children'
44330                 }
44331             });
44332         }
44333         
44334         var store  = this.store;
44335         if (i > 0) {
44336             store  = new Roo.data.SimpleStore({
44337                 //fields : this.store.reader.meta.fields,
44338                 reader : this.store.reader,
44339                 data : [ ]
44340             });
44341         }
44342         this.stores[i]  = store;
44343                   
44344         var view = this.views[i] = new Roo.View(
44345             il,
44346             this.tpl,
44347             {
44348                 singleSelect:true,
44349                 store: store,
44350                 selectedClass: this.selectedClass
44351             }
44352         );
44353         view.getEl().setWidth(lw);
44354         view.getEl().setStyle({
44355             position: i < 1 ? 'relative' : 'absolute',
44356             top: 0,
44357             left: (i * lw ) + 'px',
44358             display : i > 0 ? 'none' : 'block'
44359         });
44360         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44361         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44362         //view.on('click', this.onViewClick, this, { list : i });
44363
44364         store.on('beforeload', this.onBeforeLoad, this);
44365         store.on('load',  this.onLoad, this, { list  : i});
44366         store.on('loadexception', this.onLoadException, this);
44367
44368         // hide the other vies..
44369         
44370         
44371         
44372     },
44373       
44374     restrictHeight : function()
44375     {
44376         var mh = 0;
44377         Roo.each(this.innerLists, function(il,i) {
44378             var el = this.views[i].getEl();
44379             el.dom.style.height = '';
44380             var inner = el.dom;
44381             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44382             // only adjust heights on other ones..
44383             mh = Math.max(h, mh);
44384             if (i < 1) {
44385                 
44386                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44387                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44388                
44389             }
44390             
44391             
44392         }, this);
44393         
44394         this.list.beginUpdate();
44395         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44396         this.list.alignTo(this.el, this.listAlign);
44397         this.list.endUpdate();
44398         
44399     },
44400      
44401     
44402     // -- store handlers..
44403     // private
44404     onBeforeLoad : function()
44405     {
44406         if(!this.hasFocus){
44407             return;
44408         }
44409         this.innerLists[0].update(this.loadingText ?
44410                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44411         this.restrictHeight();
44412         this.selectedIndex = -1;
44413     },
44414     // private
44415     onLoad : function(a,b,c,d)
44416     {
44417         if (!this.loadingChildren) {
44418             // then we are loading the top level. - hide the children
44419             for (var i = 1;i < this.views.length; i++) {
44420                 this.views[i].getEl().setStyle({ display : 'none' });
44421             }
44422             var lw = Math.floor(
44423                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44424             );
44425         
44426              this.list.setWidth(lw); // default to '1'
44427
44428             
44429         }
44430         if(!this.hasFocus){
44431             return;
44432         }
44433         
44434         if(this.store.getCount() > 0) {
44435             this.expand();
44436             this.restrictHeight();   
44437         } else {
44438             this.onEmptyResults();
44439         }
44440         
44441         if (!this.loadingChildren) {
44442             this.selectActive();
44443         }
44444         /*
44445         this.stores[1].loadData([]);
44446         this.stores[2].loadData([]);
44447         this.views
44448         */    
44449     
44450         //this.el.focus();
44451     },
44452     
44453     
44454     // private
44455     onLoadException : function()
44456     {
44457         this.collapse();
44458         Roo.log(this.store.reader.jsonData);
44459         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44460             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44461         }
44462         
44463         
44464     },
44465     // no cleaning of leading spaces on blur here.
44466     cleanLeadingSpace : function(e) { },
44467     
44468
44469     onSelectChange : function (view, sels, opts )
44470     {
44471         var ix = view.getSelectedIndexes();
44472          
44473         if (opts.list > this.maxColumns - 2) {
44474             if (view.store.getCount()<  1) {
44475                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44476
44477             } else  {
44478                 if (ix.length) {
44479                     // used to clear ?? but if we are loading unselected 
44480                     this.setFromData(view.store.getAt(ix[0]).data);
44481                 }
44482                 
44483             }
44484             
44485             return;
44486         }
44487         
44488         if (!ix.length) {
44489             // this get's fired when trigger opens..
44490            // this.setFromData({});
44491             var str = this.stores[opts.list+1];
44492             str.data.clear(); // removeall wihtout the fire events..
44493             return;
44494         }
44495         
44496         var rec = view.store.getAt(ix[0]);
44497          
44498         this.setFromData(rec.data);
44499         this.fireEvent('select', this, rec, ix[0]);
44500         
44501         var lw = Math.floor(
44502              (
44503                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44504              ) / this.maxColumns
44505         );
44506         this.loadingChildren = true;
44507         this.stores[opts.list+1].loadDataFromChildren( rec );
44508         this.loadingChildren = false;
44509         var dl = this.stores[opts.list+1]. getTotalCount();
44510         
44511         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44512         
44513         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44514         for (var i = opts.list+2; i < this.views.length;i++) {
44515             this.views[i].getEl().setStyle({ display : 'none' });
44516         }
44517         
44518         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44519         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44520         
44521         if (this.isLoading) {
44522            // this.selectActive(opts.list);
44523         }
44524          
44525     },
44526     
44527     
44528     
44529     
44530     onDoubleClick : function()
44531     {
44532         this.collapse(); //??
44533     },
44534     
44535      
44536     
44537     
44538     
44539     // private
44540     recordToStack : function(store, prop, value, stack)
44541     {
44542         var cstore = new Roo.data.SimpleStore({
44543             //fields : this.store.reader.meta.fields, // we need array reader.. for
44544             reader : this.store.reader,
44545             data : [ ]
44546         });
44547         var _this = this;
44548         var record  = false;
44549         var srec = false;
44550         if(store.getCount() < 1){
44551             return false;
44552         }
44553         store.each(function(r){
44554             if(r.data[prop] == value){
44555                 record = r;
44556             srec = r;
44557                 return false;
44558             }
44559             if (r.data.cn && r.data.cn.length) {
44560                 cstore.loadDataFromChildren( r);
44561                 var cret = _this.recordToStack(cstore, prop, value, stack);
44562                 if (cret !== false) {
44563                     record = cret;
44564                     srec = r;
44565                     return false;
44566                 }
44567             }
44568              
44569             return true;
44570         });
44571         if (record == false) {
44572             return false
44573         }
44574         stack.unshift(srec);
44575         return record;
44576     },
44577     
44578     /*
44579      * find the stack of stores that match our value.
44580      *
44581      * 
44582      */
44583     
44584     selectActive : function ()
44585     {
44586         // if store is not loaded, then we will need to wait for that to happen first.
44587         var stack = [];
44588         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44589         for (var i = 0; i < stack.length; i++ ) {
44590             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44591         }
44592         
44593     }
44594         
44595          
44596     
44597     
44598     
44599     
44600 });/*
44601  * Based on:
44602  * Ext JS Library 1.1.1
44603  * Copyright(c) 2006-2007, Ext JS, LLC.
44604  *
44605  * Originally Released Under LGPL - original licence link has changed is not relivant.
44606  *
44607  * Fork - LGPL
44608  * <script type="text/javascript">
44609  */
44610 /**
44611  * @class Roo.form.Checkbox
44612  * @extends Roo.form.Field
44613  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44614  * @constructor
44615  * Creates a new Checkbox
44616  * @param {Object} config Configuration options
44617  */
44618 Roo.form.Checkbox = function(config){
44619     Roo.form.Checkbox.superclass.constructor.call(this, config);
44620     this.addEvents({
44621         /**
44622          * @event check
44623          * Fires when the checkbox is checked or unchecked.
44624              * @param {Roo.form.Checkbox} this This checkbox
44625              * @param {Boolean} checked The new checked value
44626              */
44627         check : true
44628     });
44629 };
44630
44631 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44632     /**
44633      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44634      */
44635     focusClass : undefined,
44636     /**
44637      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44638      */
44639     fieldClass: "x-form-field",
44640     /**
44641      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44642      */
44643     checked: false,
44644     /**
44645      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44646      * {tag: "input", type: "checkbox", autocomplete: "off"})
44647      */
44648     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44649     /**
44650      * @cfg {String} boxLabel The text that appears beside the checkbox
44651      */
44652     boxLabel : "",
44653     /**
44654      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44655      */  
44656     inputValue : '1',
44657     /**
44658      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44659      */
44660      valueOff: '0', // value when not checked..
44661
44662     actionMode : 'viewEl', 
44663     //
44664     // private
44665     itemCls : 'x-menu-check-item x-form-item',
44666     groupClass : 'x-menu-group-item',
44667     inputType : 'hidden',
44668     
44669     
44670     inSetChecked: false, // check that we are not calling self...
44671     
44672     inputElement: false, // real input element?
44673     basedOn: false, // ????
44674     
44675     isFormField: true, // not sure where this is needed!!!!
44676
44677     onResize : function(){
44678         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44679         if(!this.boxLabel){
44680             this.el.alignTo(this.wrap, 'c-c');
44681         }
44682     },
44683
44684     initEvents : function(){
44685         Roo.form.Checkbox.superclass.initEvents.call(this);
44686         this.el.on("click", this.onClick,  this);
44687         this.el.on("change", this.onClick,  this);
44688     },
44689
44690
44691     getResizeEl : function(){
44692         return this.wrap;
44693     },
44694
44695     getPositionEl : function(){
44696         return this.wrap;
44697     },
44698
44699     // private
44700     onRender : function(ct, position){
44701         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44702         /*
44703         if(this.inputValue !== undefined){
44704             this.el.dom.value = this.inputValue;
44705         }
44706         */
44707         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44708         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44709         var viewEl = this.wrap.createChild({ 
44710             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44711         this.viewEl = viewEl;   
44712         this.wrap.on('click', this.onClick,  this); 
44713         
44714         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44715         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44716         
44717         
44718         
44719         if(this.boxLabel){
44720             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44721         //    viewEl.on('click', this.onClick,  this); 
44722         }
44723         //if(this.checked){
44724             this.setChecked(this.checked);
44725         //}else{
44726             //this.checked = this.el.dom;
44727         //}
44728
44729     },
44730
44731     // private
44732     initValue : Roo.emptyFn,
44733
44734     /**
44735      * Returns the checked state of the checkbox.
44736      * @return {Boolean} True if checked, else false
44737      */
44738     getValue : function(){
44739         if(this.el){
44740             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44741         }
44742         return this.valueOff;
44743         
44744     },
44745
44746         // private
44747     onClick : function(){ 
44748         if (this.disabled) {
44749             return;
44750         }
44751         this.setChecked(!this.checked);
44752
44753         //if(this.el.dom.checked != this.checked){
44754         //    this.setValue(this.el.dom.checked);
44755        // }
44756     },
44757
44758     /**
44759      * Sets the checked state of the checkbox.
44760      * On is always based on a string comparison between inputValue and the param.
44761      * @param {Boolean/String} value - the value to set 
44762      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44763      */
44764     setValue : function(v,suppressEvent){
44765         
44766         
44767         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44768         //if(this.el && this.el.dom){
44769         //    this.el.dom.checked = this.checked;
44770         //    this.el.dom.defaultChecked = this.checked;
44771         //}
44772         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44773         //this.fireEvent("check", this, this.checked);
44774     },
44775     // private..
44776     setChecked : function(state,suppressEvent)
44777     {
44778         if (this.inSetChecked) {
44779             this.checked = state;
44780             return;
44781         }
44782         
44783     
44784         if(this.wrap){
44785             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44786         }
44787         this.checked = state;
44788         if(suppressEvent !== true){
44789             this.fireEvent('check', this, state);
44790         }
44791         this.inSetChecked = true;
44792         this.el.dom.value = state ? this.inputValue : this.valueOff;
44793         this.inSetChecked = false;
44794         
44795     },
44796     // handle setting of hidden value by some other method!!?!?
44797     setFromHidden: function()
44798     {
44799         if(!this.el){
44800             return;
44801         }
44802         //console.log("SET FROM HIDDEN");
44803         //alert('setFrom hidden');
44804         this.setValue(this.el.dom.value);
44805     },
44806     
44807     onDestroy : function()
44808     {
44809         if(this.viewEl){
44810             Roo.get(this.viewEl).remove();
44811         }
44812          
44813         Roo.form.Checkbox.superclass.onDestroy.call(this);
44814     },
44815     
44816     setBoxLabel : function(str)
44817     {
44818         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44819     }
44820
44821 });/*
44822  * Based on:
44823  * Ext JS Library 1.1.1
44824  * Copyright(c) 2006-2007, Ext JS, LLC.
44825  *
44826  * Originally Released Under LGPL - original licence link has changed is not relivant.
44827  *
44828  * Fork - LGPL
44829  * <script type="text/javascript">
44830  */
44831  
44832 /**
44833  * @class Roo.form.Radio
44834  * @extends Roo.form.Checkbox
44835  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44836  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44837  * @constructor
44838  * Creates a new Radio
44839  * @param {Object} config Configuration options
44840  */
44841 Roo.form.Radio = function(){
44842     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44843 };
44844 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44845     inputType: 'radio',
44846
44847     /**
44848      * If this radio is part of a group, it will return the selected value
44849      * @return {String}
44850      */
44851     getGroupValue : function(){
44852         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44853     },
44854     
44855     
44856     onRender : function(ct, position){
44857         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44858         
44859         if(this.inputValue !== undefined){
44860             this.el.dom.value = this.inputValue;
44861         }
44862          
44863         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44864         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44865         //var viewEl = this.wrap.createChild({ 
44866         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44867         //this.viewEl = viewEl;   
44868         //this.wrap.on('click', this.onClick,  this); 
44869         
44870         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44871         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44872         
44873         
44874         
44875         if(this.boxLabel){
44876             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44877         //    viewEl.on('click', this.onClick,  this); 
44878         }
44879          if(this.checked){
44880             this.el.dom.checked =   'checked' ;
44881         }
44882          
44883     } 
44884     
44885     
44886 });Roo.rtf = {}; // namespace
44887 Roo.rtf.Hex = function(hex)
44888 {
44889     this.hexstr = hex;
44890 };
44891 Roo.rtf.Paragraph = function(opts)
44892 {
44893     this.content = []; ///??? is that used?
44894 };Roo.rtf.Span = function(opts)
44895 {
44896     this.value = opts.value;
44897 };
44898
44899 Roo.rtf.Group = function(parent)
44900 {
44901     // we dont want to acutally store parent - it will make debug a nightmare..
44902     this.content = [];
44903     this.cn  = [];
44904      
44905        
44906     
44907 };
44908
44909 Roo.rtf.Group.prototype = {
44910     ignorable : false,
44911     content: false,
44912     cn: false,
44913     addContent : function(node) {
44914         // could set styles...
44915         this.content.push(node);
44916     },
44917     addChild : function(cn)
44918     {
44919         this.cn.push(cn);
44920     },
44921     // only for images really...
44922     toDataURL : function()
44923     {
44924         var mimetype = false;
44925         switch(true) {
44926             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44927                 mimetype = "image/png";
44928                 break;
44929              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44930                 mimetype = "image/jpeg";
44931                 break;
44932             default :
44933                 return 'about:blank'; // ?? error?
44934         }
44935         
44936         
44937         var hexstring = this.content[this.content.length-1].value;
44938         
44939         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44940             return String.fromCharCode(parseInt(a, 16));
44941         }).join(""));
44942     }
44943     
44944 };
44945 // this looks like it's normally the {rtf{ .... }}
44946 Roo.rtf.Document = function()
44947 {
44948     // we dont want to acutally store parent - it will make debug a nightmare..
44949     this.rtlch  = [];
44950     this.content = [];
44951     this.cn = [];
44952     
44953 };
44954 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44955     addChild : function(cn)
44956     {
44957         this.cn.push(cn);
44958         switch(cn.type) {
44959             case 'rtlch': // most content seems to be inside this??
44960             case 'listtext':
44961             case 'shpinst':
44962                 this.rtlch.push(cn);
44963                 return;
44964             default:
44965                 this[cn.type] = cn;
44966         }
44967         
44968     },
44969     
44970     getElementsByType : function(type)
44971     {
44972         var ret =  [];
44973         this._getElementsByType(type, ret, this.cn, 'rtf');
44974         return ret;
44975     },
44976     _getElementsByType : function (type, ret, search_array, path)
44977     {
44978         search_array.forEach(function(n,i) {
44979             if (n.type == type) {
44980                 n.path = path + '/' + n.type + ':' + i;
44981                 ret.push(n);
44982             }
44983             if (n.cn.length > 0) {
44984                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44985             }
44986         },this);
44987     }
44988     
44989 });
44990  
44991 Roo.rtf.Ctrl = function(opts)
44992 {
44993     this.value = opts.value;
44994     this.param = opts.param;
44995 };
44996 /**
44997  *
44998  *
44999  * based on this https://github.com/iarna/rtf-parser
45000  * it's really only designed to extract pict from pasted RTF 
45001  *
45002  * usage:
45003  *
45004  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45005  *  
45006  *
45007  */
45008
45009  
45010
45011
45012
45013 Roo.rtf.Parser = function(text) {
45014     //super({objectMode: true})
45015     this.text = '';
45016     this.parserState = this.parseText;
45017     
45018     // these are for interpeter...
45019     this.doc = {};
45020     ///this.parserState = this.parseTop
45021     this.groupStack = [];
45022     this.hexStore = [];
45023     this.doc = false;
45024     
45025     this.groups = []; // where we put the return.
45026     
45027     for (var ii = 0; ii < text.length; ++ii) {
45028         ++this.cpos;
45029         
45030         if (text[ii] === '\n') {
45031             ++this.row;
45032             this.col = 1;
45033         } else {
45034             ++this.col;
45035         }
45036         this.parserState(text[ii]);
45037     }
45038     
45039     
45040     
45041 };
45042 Roo.rtf.Parser.prototype = {
45043     text : '', // string being parsed..
45044     controlWord : '',
45045     controlWordParam :  '',
45046     hexChar : '',
45047     doc : false,
45048     group: false,
45049     groupStack : false,
45050     hexStore : false,
45051     
45052     
45053     cpos : 0, 
45054     row : 1, // reportin?
45055     col : 1, //
45056
45057      
45058     push : function (el)
45059     {
45060         var m = 'cmd'+ el.type;
45061         if (typeof(this[m]) == 'undefined') {
45062             Roo.log('invalid cmd:' + el.type);
45063             return;
45064         }
45065         this[m](el);
45066         //Roo.log(el);
45067     },
45068     flushHexStore : function()
45069     {
45070         if (this.hexStore.length < 1) {
45071             return;
45072         }
45073         var hexstr = this.hexStore.map(
45074             function(cmd) {
45075                 return cmd.value;
45076         }).join('');
45077         
45078         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45079               
45080             
45081         this.hexStore.splice(0)
45082         
45083     },
45084     
45085     cmdgroupstart : function()
45086     {
45087         this.flushHexStore();
45088         if (this.group) {
45089             this.groupStack.push(this.group);
45090         }
45091          // parent..
45092         if (this.doc === false) {
45093             this.group = this.doc = new Roo.rtf.Document();
45094             return;
45095             
45096         }
45097         this.group = new Roo.rtf.Group(this.group);
45098     },
45099     cmdignorable : function()
45100     {
45101         this.flushHexStore();
45102         this.group.ignorable = true;
45103     },
45104     cmdendparagraph : function()
45105     {
45106         this.flushHexStore();
45107         this.group.addContent(new Roo.rtf.Paragraph());
45108     },
45109     cmdgroupend : function ()
45110     {
45111         this.flushHexStore();
45112         var endingGroup = this.group;
45113         
45114         
45115         this.group = this.groupStack.pop();
45116         if (this.group) {
45117             this.group.addChild(endingGroup);
45118         }
45119         
45120         
45121         
45122         var doc = this.group || this.doc;
45123         //if (endingGroup instanceof FontTable) {
45124         //  doc.fonts = endingGroup.table
45125         //} else if (endingGroup instanceof ColorTable) {
45126         //  doc.colors = endingGroup.table
45127         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45128         if (endingGroup.ignorable === false) {
45129             //code
45130             this.groups.push(endingGroup);
45131            // Roo.log( endingGroup );
45132         }
45133             //Roo.each(endingGroup.content, function(item)) {
45134             //    doc.addContent(item);
45135             //}
45136             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45137         //}
45138     },
45139     cmdtext : function (cmd)
45140     {
45141         this.flushHexStore();
45142         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45143             //this.group = this.doc
45144         }
45145         this.group.addContent(new Roo.rtf.Span(cmd));
45146     },
45147     cmdcontrolword : function (cmd)
45148     {
45149         this.flushHexStore();
45150         if (!this.group.type) {
45151             this.group.type = cmd.value;
45152             return;
45153         }
45154         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45155         // we actually don't care about ctrl words...
45156         return ;
45157         /*
45158         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45159         if (this[method]) {
45160             this[method](cmd.param)
45161         } else {
45162             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45163         }
45164         */
45165     },
45166     cmdhexchar : function(cmd) {
45167         this.hexStore.push(cmd);
45168     },
45169     cmderror : function(cmd) {
45170         throw new Exception (cmd.value);
45171     },
45172     
45173     /*
45174       _flush (done) {
45175         if (this.text !== '\u0000') this.emitText()
45176         done()
45177       }
45178       */
45179       
45180       
45181     parseText : function(c)
45182     {
45183         if (c === '\\') {
45184             this.parserState = this.parseEscapes;
45185         } else if (c === '{') {
45186             this.emitStartGroup();
45187         } else if (c === '}') {
45188             this.emitEndGroup();
45189         } else if (c === '\x0A' || c === '\x0D') {
45190             // cr/lf are noise chars
45191         } else {
45192             this.text += c;
45193         }
45194     },
45195     
45196     parseEscapes: function (c)
45197     {
45198         if (c === '\\' || c === '{' || c === '}') {
45199             this.text += c;
45200             this.parserState = this.parseText;
45201         } else {
45202             this.parserState = this.parseControlSymbol;
45203             this.parseControlSymbol(c);
45204         }
45205     },
45206     parseControlSymbol: function(c)
45207     {
45208         if (c === '~') {
45209             this.text += '\u00a0'; // nbsp
45210             this.parserState = this.parseText
45211         } else if (c === '-') {
45212              this.text += '\u00ad'; // soft hyphen
45213         } else if (c === '_') {
45214             this.text += '\u2011'; // non-breaking hyphen
45215         } else if (c === '*') {
45216             this.emitIgnorable();
45217             this.parserState = this.parseText;
45218         } else if (c === "'") {
45219             this.parserState = this.parseHexChar;
45220         } else if (c === '|') { // formula cacter
45221             this.emitFormula();
45222             this.parserState = this.parseText;
45223         } else if (c === ':') { // subentry in an index entry
45224             this.emitIndexSubEntry();
45225             this.parserState = this.parseText;
45226         } else if (c === '\x0a') {
45227             this.emitEndParagraph();
45228             this.parserState = this.parseText;
45229         } else if (c === '\x0d') {
45230             this.emitEndParagraph();
45231             this.parserState = this.parseText;
45232         } else {
45233             this.parserState = this.parseControlWord;
45234             this.parseControlWord(c);
45235         }
45236     },
45237     parseHexChar: function (c)
45238     {
45239         if (/^[A-Fa-f0-9]$/.test(c)) {
45240             this.hexChar += c;
45241             if (this.hexChar.length >= 2) {
45242               this.emitHexChar();
45243               this.parserState = this.parseText;
45244             }
45245             return;
45246         }
45247         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45248         this.parserState = this.parseText;
45249         
45250     },
45251     parseControlWord : function(c)
45252     {
45253         if (c === ' ') {
45254             this.emitControlWord();
45255             this.parserState = this.parseText;
45256         } else if (/^[-\d]$/.test(c)) {
45257             this.parserState = this.parseControlWordParam;
45258             this.controlWordParam += c;
45259         } else if (/^[A-Za-z]$/.test(c)) {
45260           this.controlWord += c;
45261         } else {
45262           this.emitControlWord();
45263           this.parserState = this.parseText;
45264           this.parseText(c);
45265         }
45266     },
45267     parseControlWordParam : function (c) {
45268         if (/^\d$/.test(c)) {
45269           this.controlWordParam += c;
45270         } else if (c === ' ') {
45271           this.emitControlWord();
45272           this.parserState = this.parseText;
45273         } else {
45274           this.emitControlWord();
45275           this.parserState = this.parseText;
45276           this.parseText(c);
45277         }
45278     },
45279     
45280     
45281     
45282     
45283     emitText : function () {
45284         if (this.text === '') {
45285             return;
45286         }
45287         this.push({
45288             type: 'text',
45289             value: this.text,
45290             pos: this.cpos,
45291             row: this.row,
45292             col: this.col
45293         });
45294         this.text = ''
45295     },
45296     emitControlWord : function ()
45297     {
45298         this.emitText();
45299         if (this.controlWord === '') {
45300             this.emitError('empty control word');
45301         } else {
45302             this.push({
45303                   type: 'controlword',
45304                   value: this.controlWord,
45305                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45306                   pos: this.cpos,
45307                   row: this.row,
45308                   col: this.col
45309             });
45310         }
45311         this.controlWord = '';
45312         this.controlWordParam = '';
45313     },
45314     emitStartGroup : function ()
45315     {
45316         this.emitText();
45317         this.push({
45318             type: 'groupstart',
45319             pos: this.cpos,
45320             row: this.row,
45321             col: this.col
45322         });
45323     },
45324     emitEndGroup : function ()
45325     {
45326         this.emitText();
45327         this.push({
45328             type: 'groupend',
45329             pos: this.cpos,
45330             row: this.row,
45331             col: this.col
45332         });
45333     },
45334     emitIgnorable : function ()
45335     {
45336         this.emitText();
45337         this.push({
45338             type: 'ignorable',
45339             pos: this.cpos,
45340             row: this.row,
45341             col: this.col
45342         });
45343     },
45344     emitHexChar : function ()
45345     {
45346         this.emitText();
45347         this.push({
45348             type: 'hexchar',
45349             value: this.hexChar,
45350             pos: this.cpos,
45351             row: this.row,
45352             col: this.col
45353         });
45354         this.hexChar = ''
45355     },
45356     emitError : function (message)
45357     {
45358       this.emitText();
45359       this.push({
45360             type: 'error',
45361             value: message,
45362             row: this.row,
45363             col: this.col,
45364             char: this.cpos //,
45365             //stack: new Error().stack
45366         });
45367     },
45368     emitEndParagraph : function () {
45369         this.emitText();
45370         this.push({
45371             type: 'endparagraph',
45372             pos: this.cpos,
45373             row: this.row,
45374             col: this.col
45375         });
45376     }
45377      
45378 } ;
45379 Roo.htmleditor = {};
45380  
45381 /**
45382  * @class Roo.htmleditor.Filter
45383  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45384  * @cfg {DomElement} node The node to iterate and filter
45385  * @cfg {boolean|String|Array} tag Tags to replace 
45386  * @constructor
45387  * Create a new Filter.
45388  * @param {Object} config Configuration options
45389  */
45390
45391
45392
45393 Roo.htmleditor.Filter = function(cfg) {
45394     Roo.apply(this.cfg);
45395     // this does not actually call walk as it's really just a abstract class
45396 }
45397
45398
45399 Roo.htmleditor.Filter.prototype = {
45400     
45401     node: false,
45402     
45403     tag: false,
45404
45405     // overrride to do replace comments.
45406     replaceComment : false,
45407     
45408     // overrride to do replace or do stuff with tags..
45409     replaceTag : false,
45410     
45411     walk : function(dom)
45412     {
45413         Roo.each( Array.from(dom.childNodes), function( e ) {
45414             switch(true) {
45415                 
45416                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45417                     this.replaceComment(e);
45418                     return;
45419                 
45420                 case e.nodeType != 1: //not a node.
45421                     return;
45422                 
45423                 case this.tag === true: // everything
45424                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45425                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45426                     if (this.replaceTag && false === this.replaceTag(e)) {
45427                         return;
45428                     }
45429                     if (e.hasChildNodes()) {
45430                         this.walk(e);
45431                     }
45432                     return;
45433                 
45434                 default:    // tags .. that do not match.
45435                     if (e.hasChildNodes()) {
45436                         this.walk(e);
45437                     }
45438             }
45439             
45440         }, this);
45441         
45442     }
45443 }; 
45444
45445 /**
45446  * @class Roo.htmleditor.FilterAttributes
45447  * clean attributes and  styles including http:// etc.. in attribute
45448  * @constructor
45449 * Run a new Attribute Filter
45450 * @param {Object} config Configuration options
45451  */
45452 Roo.htmleditor.FilterAttributes = function(cfg)
45453 {
45454     Roo.apply(this, cfg);
45455     this.attrib_black = this.attrib_black || [];
45456     this.attrib_white = this.attrib_white || [];
45457
45458     this.attrib_clean = this.attrib_clean || [];
45459     this.style_white = this.style_white || [];
45460     this.style_black = this.style_black || [];
45461     this.walk(cfg.node);
45462 }
45463
45464 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45465 {
45466     tag: true, // all tags
45467     
45468     attrib_black : false, // array
45469     attrib_clean : false,
45470     attrib_white : false,
45471
45472     style_white : false,
45473     style_black : false,
45474      
45475      
45476     replaceTag : function(node)
45477     {
45478         if (!node.attributes || !node.attributes.length) {
45479             return true;
45480         }
45481         
45482         for (var i = node.attributes.length-1; i > -1 ; i--) {
45483             var a = node.attributes[i];
45484             //console.log(a);
45485             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45486                 node.removeAttribute(a.name);
45487                 continue;
45488             }
45489             
45490             
45491             
45492             if (a.name.toLowerCase().substr(0,2)=='on')  {
45493                 node.removeAttribute(a.name);
45494                 continue;
45495             }
45496             
45497             
45498             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45499                 node.removeAttribute(a.name);
45500                 continue;
45501             }
45502             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45503                 this.cleanAttr(node,a.name,a.value); // fixme..
45504                 continue;
45505             }
45506             if (a.name == 'style') {
45507                 this.cleanStyle(node,a.name,a.value);
45508                 continue;
45509             }
45510             /// clean up MS crap..
45511             // tecnically this should be a list of valid class'es..
45512             
45513             
45514             if (a.name == 'class') {
45515                 if (a.value.match(/^Mso/)) {
45516                     node.removeAttribute('class');
45517                 }
45518                 
45519                 if (a.value.match(/^body$/)) {
45520                     node.removeAttribute('class');
45521                 }
45522                 continue;
45523             }
45524             
45525             
45526             // style cleanup!?
45527             // class cleanup?
45528             
45529         }
45530         return true; // clean children
45531     },
45532         
45533     cleanAttr: function(node, n,v)
45534     {
45535         
45536         if (v.match(/^\./) || v.match(/^\//)) {
45537             return;
45538         }
45539         if (v.match(/^(http|https):\/\//)
45540             || v.match(/^mailto:/) 
45541             || v.match(/^ftp:/)
45542             || v.match(/^data:/)
45543             ) {
45544             return;
45545         }
45546         if (v.match(/^#/)) {
45547             return;
45548         }
45549         if (v.match(/^\{/)) { // allow template editing.
45550             return;
45551         }
45552 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45553         node.removeAttribute(n);
45554         
45555     },
45556     cleanStyle : function(node,  n,v)
45557     {
45558         if (v.match(/expression/)) { //XSS?? should we even bother..
45559             node.removeAttribute(n);
45560             return;
45561         }
45562         
45563         var parts = v.split(/;/);
45564         var clean = [];
45565         
45566         Roo.each(parts, function(p) {
45567             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45568             if (!p.length) {
45569                 return true;
45570             }
45571             var l = p.split(':').shift().replace(/\s+/g,'');
45572             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45573             
45574             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45575                 return true;
45576             }
45577             //Roo.log()
45578             // only allow 'c whitelisted system attributes'
45579             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45580                 return true;
45581             }
45582             
45583             
45584             clean.push(p);
45585             return true;
45586         },this);
45587         if (clean.length) { 
45588             node.setAttribute(n, clean.join(';'));
45589         } else {
45590             node.removeAttribute(n);
45591         }
45592         
45593     }
45594         
45595         
45596         
45597     
45598 });/**
45599  * @class Roo.htmleditor.FilterBlack
45600  * remove blacklisted elements.
45601  * @constructor
45602  * Run a new Blacklisted Filter
45603  * @param {Object} config Configuration options
45604  */
45605
45606 Roo.htmleditor.FilterBlack = function(cfg)
45607 {
45608     Roo.apply(this, cfg);
45609     this.walk(cfg.node);
45610 }
45611
45612 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45613 {
45614     tag : true, // all elements.
45615    
45616     replace : function(n)
45617     {
45618         n.parentNode.removeChild(n);
45619     }
45620 });
45621 /**
45622  * @class Roo.htmleditor.FilterComment
45623  * remove comments.
45624  * @constructor
45625 * Run a new Comments Filter
45626 * @param {Object} config Configuration options
45627  */
45628 Roo.htmleditor.FilterComment = function(cfg)
45629 {
45630     this.walk(cfg.node);
45631 }
45632
45633 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45634 {
45635   
45636     replaceComment : function(n)
45637     {
45638         n.parentNode.removeChild(n);
45639     }
45640 });/**
45641  * @class Roo.htmleditor.FilterKeepChildren
45642  * remove tags but keep children
45643  * @constructor
45644  * Run a new Keep Children Filter
45645  * @param {Object} config Configuration options
45646  */
45647
45648 Roo.htmleditor.FilterKeepChildren = function(cfg)
45649 {
45650     Roo.apply(this, cfg);
45651     if (this.tag === false) {
45652         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45653     }
45654     this.walk(cfg.node);
45655 }
45656
45657 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45658 {
45659     
45660   
45661     replaceTag : function(node)
45662     {
45663         // walk children...
45664         //Roo.log(node);
45665         var ar = Array.from(node.childNodes);
45666         //remove first..
45667         for (var i = 0; i < ar.length; i++) {
45668             if (ar[i].nodeType == 1) {
45669                 if (
45670                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45671                     || // array and it matches
45672                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45673                 ) {
45674                     this.replaceTag(ar[i]); // child is blacklisted as well...
45675                     continue;
45676                 }
45677             }
45678         }  
45679         ar = Array.from(node.childNodes);
45680         for (var i = 0; i < ar.length; i++) {
45681          
45682             node.removeChild(ar[i]);
45683             // what if we need to walk these???
45684             node.parentNode.insertBefore(ar[i], node);
45685             if (this.tag !== false) {
45686                 this.walk(ar[i]);
45687                 
45688             }
45689         }
45690         node.parentNode.removeChild(node);
45691         return false; // don't walk children
45692         
45693         
45694     }
45695 });/**
45696  * @class Roo.htmleditor.FilterParagraph
45697  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45698  * like on 'push' to remove the <p> tags and replace them with line breaks.
45699  * @constructor
45700  * Run a new Paragraph Filter
45701  * @param {Object} config Configuration options
45702  */
45703
45704 Roo.htmleditor.FilterParagraph = function(cfg)
45705 {
45706     // no need to apply config.
45707     this.walk(cfg.node);
45708 }
45709
45710 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45711 {
45712     
45713      
45714     tag : 'P',
45715     
45716      
45717     replaceTag : function(node)
45718     {
45719         
45720         if (node.childNodes.length == 1 &&
45721             node.childNodes[0].nodeType == 3 &&
45722             node.childNodes[0].textContent.trim().length < 1
45723             ) {
45724             // remove and replace with '<BR>';
45725             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45726             return false; // no need to walk..
45727         }
45728         var ar = Array.from(node.childNodes);
45729         for (var i = 0; i < ar.length; i++) {
45730             node.removeChild(ar[i]);
45731             // what if we need to walk these???
45732             node.parentNode.insertBefore(ar[i], node);
45733         }
45734         // now what about this?
45735         // <p> &nbsp; </p>
45736         
45737         // double BR.
45738         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45739         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45740         node.parentNode.removeChild(node);
45741         
45742         return false;
45743
45744     }
45745     
45746 });/**
45747  * @class Roo.htmleditor.FilterSpan
45748  * filter span's with no attributes out..
45749  * @constructor
45750  * Run a new Span Filter
45751  * @param {Object} config Configuration options
45752  */
45753
45754 Roo.htmleditor.FilterSpan = function(cfg)
45755 {
45756     // no need to apply config.
45757     this.walk(cfg.node);
45758 }
45759
45760 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45761 {
45762      
45763     tag : 'SPAN',
45764      
45765  
45766     replaceTag : function(node)
45767     {
45768         if (node.attributes && node.attributes.length > 0) {
45769             return true; // walk if there are any.
45770         }
45771         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45772         return false;
45773      
45774     }
45775     
45776 });/**
45777  * @class Roo.htmleditor.FilterTableWidth
45778   try and remove table width data - as that frequently messes up other stuff.
45779  * 
45780  *      was cleanTableWidths.
45781  *
45782  * Quite often pasting from word etc.. results in tables with column and widths.
45783  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45784  *
45785  * @constructor
45786  * Run a new Table Filter
45787  * @param {Object} config Configuration options
45788  */
45789
45790 Roo.htmleditor.FilterTableWidth = function(cfg)
45791 {
45792     // no need to apply config.
45793     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45794     this.walk(cfg.node);
45795 }
45796
45797 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45798 {
45799      
45800      
45801     
45802     replaceTag: function(node) {
45803         
45804         
45805       
45806         if (node.hasAttribute('width')) {
45807             node.removeAttribute('width');
45808         }
45809         
45810          
45811         if (node.hasAttribute("style")) {
45812             // pretty basic...
45813             
45814             var styles = node.getAttribute("style").split(";");
45815             var nstyle = [];
45816             Roo.each(styles, function(s) {
45817                 if (!s.match(/:/)) {
45818                     return;
45819                 }
45820                 var kv = s.split(":");
45821                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45822                     return;
45823                 }
45824                 // what ever is left... we allow.
45825                 nstyle.push(s);
45826             });
45827             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45828             if (!nstyle.length) {
45829                 node.removeAttribute('style');
45830             }
45831         }
45832         
45833         return true; // continue doing children..
45834     }
45835 });/**
45836  * @class Roo.htmleditor.FilterWord
45837  * try and clean up all the mess that Word generates.
45838  * 
45839  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45840  
45841  * @constructor
45842  * Run a new Span Filter
45843  * @param {Object} config Configuration options
45844  */
45845
45846 Roo.htmleditor.FilterWord = function(cfg)
45847 {
45848     // no need to apply config.
45849     this.walk(cfg.node);
45850 }
45851
45852 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45853 {
45854     tag: true,
45855      
45856     
45857     /**
45858      * Clean up MS wordisms...
45859      */
45860     replaceTag : function(node)
45861     {
45862          
45863         // no idea what this does - span with text, replaceds with just text.
45864         if(
45865                 node.nodeName == 'SPAN' &&
45866                 !node.hasAttributes() &&
45867                 node.childNodes.length == 1 &&
45868                 node.firstChild.nodeName == "#text"  
45869         ) {
45870             var textNode = node.firstChild;
45871             node.removeChild(textNode);
45872             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45873                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45874             }
45875             node.parentNode.insertBefore(textNode, node);
45876             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45877                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45878             }
45879             
45880             node.parentNode.removeChild(node);
45881             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45882         }
45883         
45884    
45885         
45886         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45887             node.parentNode.removeChild(node);
45888             return false; // dont do chidlren
45889         }
45890         //Roo.log(node.tagName);
45891         // remove - but keep children..
45892         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45893             //Roo.log('-- removed');
45894             while (node.childNodes.length) {
45895                 var cn = node.childNodes[0];
45896                 node.removeChild(cn);
45897                 node.parentNode.insertBefore(cn, node);
45898                 // move node to parent - and clean it..
45899                 this.replaceTag(cn);
45900             }
45901             node.parentNode.removeChild(node);
45902             /// no need to iterate chidlren = it's got none..
45903             //this.iterateChildren(node, this.cleanWord);
45904             return false; // no need to iterate children.
45905         }
45906         // clean styles
45907         if (node.className.length) {
45908             
45909             var cn = node.className.split(/\W+/);
45910             var cna = [];
45911             Roo.each(cn, function(cls) {
45912                 if (cls.match(/Mso[a-zA-Z]+/)) {
45913                     return;
45914                 }
45915                 cna.push(cls);
45916             });
45917             node.className = cna.length ? cna.join(' ') : '';
45918             if (!cna.length) {
45919                 node.removeAttribute("class");
45920             }
45921         }
45922         
45923         if (node.hasAttribute("lang")) {
45924             node.removeAttribute("lang");
45925         }
45926         
45927         if (node.hasAttribute("style")) {
45928             
45929             var styles = node.getAttribute("style").split(";");
45930             var nstyle = [];
45931             Roo.each(styles, function(s) {
45932                 if (!s.match(/:/)) {
45933                     return;
45934                 }
45935                 var kv = s.split(":");
45936                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45937                     return;
45938                 }
45939                 // what ever is left... we allow.
45940                 nstyle.push(s);
45941             });
45942             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45943             if (!nstyle.length) {
45944                 node.removeAttribute('style');
45945             }
45946         }
45947         return true; // do children
45948         
45949         
45950         
45951     }
45952 });
45953 /**
45954  * @class Roo.htmleditor.FilterStyleToTag
45955  * part of the word stuff... - certain 'styles' should be converted to tags.
45956  * eg.
45957  *   font-weight: bold -> bold
45958  *   ?? super / subscrit etc..
45959  * 
45960  * @constructor
45961 * Run a new style to tag filter.
45962 * @param {Object} config Configuration options
45963  */
45964 Roo.htmleditor.FilterStyleToTag = function(cfg)
45965 {
45966     
45967     this.tags = {
45968         B  : [ 'fontWeight' , 'bold'],
45969         I :  [ 'fontStyle' , 'italic'],
45970         //pre :  [ 'font-style' , 'italic'],
45971         // h1.. h6 ?? font-size?
45972         SUP : [ 'verticalAlign' , 'super' ],
45973         SUB : [ 'verticalAlign' , 'sub' ]
45974         
45975         
45976     };
45977     
45978     Roo.apply(this, cfg);
45979      
45980     
45981     this.walk(cfg.node);
45982     
45983     
45984     
45985 }
45986
45987
45988 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45989 {
45990     tag: true, // all tags
45991     
45992     tags : false,
45993     
45994     
45995     replaceTag : function(node)
45996     {
45997         
45998         
45999         if (node.getAttribute("style") === null) {
46000             return true;
46001         }
46002         var inject = [];
46003         for (var k in this.tags) {
46004             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46005                 inject.push(k);
46006                 node.style.removeProperty(this.tags[k][0]);
46007             }
46008         }
46009         if (!inject.length) {
46010             return true; 
46011         }
46012         var cn = Array.from(node.childNodes);
46013         var nn = node;
46014         Roo.each(inject, function(t) {
46015             var nc = node.ownerDocument.createelement(t);
46016             nn.appendChild(nc);
46017             nn = nc;
46018         });
46019         for(var i = 0;i < cn.length;cn++) {
46020             node.removeChild(cn[i]);
46021             nn.appendChild(cn[i]);
46022         }
46023         return true /// iterate thru
46024     }
46025     
46026 })/**
46027  * @class Roo.htmleditor.FilterLongBr
46028  * BR/BR/BR - keep a maximum of 2...
46029  * @constructor
46030  * Run a new Long BR Filter
46031  * @param {Object} config Configuration options
46032  */
46033
46034 Roo.htmleditor.FilterLongBr = function(cfg)
46035 {
46036     // no need to apply config.
46037     this.walk(cfg.node);
46038 }
46039
46040 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46041 {
46042     
46043      
46044     tag : 'BR',
46045     
46046      
46047     replaceTag : function(node)
46048     {
46049         
46050         var ps = node.nextSibling;
46051         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46052             ps = ps.nextSibling;
46053         }
46054         
46055         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46056             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46057             return false;
46058         }
46059         
46060         if (!ps || ps.nodeType != 1) {
46061             return false;
46062         }
46063         
46064         if (!ps || ps.tagName != 'BR') {
46065            
46066             return false;
46067         }
46068         
46069         
46070         
46071         
46072         
46073         if (!node.previousSibling) {
46074             return false;
46075         }
46076         var ps = node.previousSibling;
46077         
46078         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46079             ps = ps.previousSibling;
46080         }
46081         if (!ps || ps.nodeType != 1) {
46082             return false;
46083         }
46084         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46085         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46086             return false;
46087         }
46088         
46089         node.parentNode.removeChild(node); // remove me...
46090         
46091         return false; // no need to do children
46092
46093     }
46094     
46095 });
46096 /**
46097  * @class Roo.htmleditor.Tidy
46098  * Tidy HTML 
46099  * @cfg {Roo.HtmlEditorCore} core the editor.
46100  * @constructor
46101  * Create a new Filter.
46102  * @param {Object} config Configuration options
46103  */
46104
46105
46106 Roo.htmleditor.Tidy = function(cfg) {
46107     Roo.apply(this, cfg);
46108     
46109     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46110      
46111 }
46112
46113 Roo.htmleditor.Tidy.toString = function(node)
46114 {
46115     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46116 }
46117
46118 Roo.htmleditor.Tidy.prototype = {
46119     
46120     
46121     wrap : function(s) {
46122         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46123     },
46124
46125     
46126     tidy : function(node, indent) {
46127      
46128         if  (node.nodeType == 3) {
46129             // text.
46130             
46131             
46132             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46133                 
46134             
46135         }
46136         
46137         if  (node.nodeType != 1) {
46138             return '';
46139         }
46140         
46141         
46142         
46143         if (node.tagName == 'BODY') {
46144             
46145             return this.cn(node, '');
46146         }
46147              
46148              // Prints the node tagName, such as <A>, <IMG>, etc
46149         var ret = "<" + node.tagName +  this.attr(node) ;
46150         
46151         // elements with no children..
46152         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46153                 return ret + '/>';
46154         }
46155         ret += '>';
46156         
46157         
46158         var cindent = indent === false ? '' : (indent + '  ');
46159         // tags where we will not pad the children.. (inline text tags etc..)
46160         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46161             cindent = false;
46162             
46163             
46164         }
46165         
46166         var cn = this.cn(node, cindent );
46167         
46168         return ret + cn  + '</' + node.tagName + '>';
46169         
46170     },
46171     cn: function(node, indent)
46172     {
46173         var ret = [];
46174         
46175         var ar = Array.from(node.childNodes);
46176         for (var i = 0 ; i < ar.length ; i++) {
46177             
46178             
46179             
46180             if (indent !== false   // indent==false preservies everything
46181                 && i > 0
46182                 && ar[i].nodeType == 3 
46183                 && ar[i].nodeValue.length > 0
46184                 && ar[i].nodeValue.match(/^\s+/)
46185             ) {
46186                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46187                     ret.pop(); // remove line break from last?
46188                 }
46189                 
46190                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46191             }
46192             if (indent !== false
46193                 && ar[i].nodeType == 1 // element - and indent is not set... 
46194             ) {
46195                 ret.push("\n" + indent); 
46196             }
46197             
46198             ret.push(this.tidy(ar[i], indent));
46199             // text + trailing indent 
46200             if (indent !== false
46201                 && ar[i].nodeType == 3
46202                 && ar[i].nodeValue.length > 0
46203                 && ar[i].nodeValue.match(/\s+$/)
46204             ){
46205                 ret.push("\n" + indent); 
46206             }
46207             
46208             
46209             
46210             
46211         }
46212         // what if all text?
46213         
46214         
46215         return ret.join('');
46216     },
46217     
46218          
46219         
46220     attr : function(node)
46221     {
46222         var attr = [];
46223         for(i = 0; i < node.attributes.length;i++) {
46224             
46225             // skip empty values?
46226             if (!node.attributes.item(i).value.length) {
46227                 continue;
46228             }
46229             attr.push(  node.attributes.item(i).name + '="' +
46230                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46231             );
46232         }
46233         return attr.length ? (' ' + attr.join(' ') ) : '';
46234         
46235     }
46236     
46237     
46238     
46239 }
46240 /**
46241  * @class Roo.htmleditor.KeyEnter
46242  * Handle Enter press..
46243  * @cfg {Roo.HtmlEditorCore} core the editor.
46244  * @constructor
46245  * Create a new Filter.
46246  * @param {Object} config Configuration options
46247  */
46248
46249
46250
46251 Roo.htmleditor.KeyEnter = function(cfg) {
46252     Roo.apply(this, cfg);
46253     // this does not actually call walk as it's really just a abstract class
46254  
46255     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46256 }
46257
46258
46259 Roo.htmleditor.KeyEnter.prototype = {
46260     
46261     core : false,
46262     
46263     keypress : function(e)
46264     {
46265         if (e.charCode != 13) {
46266             return true;
46267         }
46268         e.preventDefault();
46269         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46270         var doc = this.core.doc;
46271         
46272         var docFragment = doc.createDocumentFragment();
46273     
46274         //add a new line
46275         var newEle = doc.createTextNode('\n');
46276         docFragment.appendChild(newEle);
46277     
46278     
46279         var range = this.core.win.getSelection().getRangeAt(0);
46280         var n = range.commonAncestorContainer ;
46281         while (n && n.nodeType != 1) {
46282             n  = n.parentNode;
46283         }
46284         var li = false;
46285         if (n && n.tagName == 'UL') {
46286             li = doc.createElement('LI');
46287             n.appendChild(li);
46288             
46289         }
46290         if (n && n.tagName == 'LI') {
46291             li = doc.createElement('LI');
46292             if (n.nextSibling) {
46293                 n.parentNode.insertBefore(li, n.firstSibling);
46294                 
46295             } else {
46296                 n.parentNode.appendChild(li);
46297             }
46298         }
46299         if (li) {   
46300             range = doc.createRange();
46301             range.setStartAfter(li);
46302             range.collapse(true);
46303         
46304             //make the cursor there
46305             var sel = this.core.win.getSelection();
46306             sel.removeAllRanges();
46307             sel.addRange(range);
46308             return false;
46309             
46310             
46311         }
46312         //add the br, or p, or something else
46313         newEle = doc.createElement('br');
46314         docFragment.appendChild(newEle);
46315     
46316         //make the br replace selection
46317         
46318         range.deleteContents();
46319         
46320         range.insertNode(docFragment);
46321         range = range.cloneRange();
46322         range.collapse(true);
46323         var sel = this.core.win.getSelection();
46324         sel.removeAllRanges();
46325         sel.addRange(range);
46326         sel.collapseToEnd();
46327     
46328         return false;
46329          
46330     }
46331 };
46332      
46333 /**
46334  * @class Roo.htmleditor.Block
46335  * Base class for html editor blocks - do not use it directly .. extend it..
46336  * @cfg {DomElement} node The node to apply stuff to.
46337  * @cfg {String} friendly_name the name that appears in the context bar about this block
46338  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46339  
46340  * @constructor
46341  * Create a new Filter.
46342  * @param {Object} config Configuration options
46343  */
46344
46345 Roo.htmleditor.Block  = function(cfg)
46346 {
46347     // do nothing .. should not be called really.
46348 }
46349
46350 Roo.htmleditor.Block.factory = function(node)
46351 {
46352     
46353     var id = Roo.get(node).id;
46354     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46355         Roo.htmleditor.Block.cache[id].readElement();
46356         return Roo.htmleditor.Block.cache[id];
46357     }
46358     
46359     var cls = Roo.htmleditor['Block' + node.getAttribute('data-block')];
46360     if (typeof(cls) == 'undefined') {
46361         Roo.log("OOps missing block : " + 'Block' + node.getAttribute('data-block'));
46362         return false;
46363     }
46364     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46365     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46366 };
46367 // question goes here... do we need to clear out this cache sometimes?
46368 // or show we make it relivant to the htmleditor.
46369 Roo.htmleditor.Block.cache = {};
46370
46371 Roo.htmleditor.Block.prototype = {
46372     
46373     node : false,
46374     
46375      // used by context menu
46376     friendly_name : 'Image with caption',
46377     
46378     context : false,
46379     /**
46380      * Update a node with values from this object
46381      * @param {DomElement} node
46382      */
46383     updateElement : function(node)
46384     {
46385         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46386     },
46387      /**
46388      * convert to plain HTML for calling insertAtCursor..
46389      */
46390     toHTML : function()
46391     {
46392         return Roo.DomHelper.markup(this.toObject());
46393     },
46394     /**
46395      * used by readEleemnt to extract data from a node
46396      * may need improving as it's pretty basic
46397      
46398      * @param {DomElement} node
46399      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46400      * @param {String} attribute (use html - for contents, or style for using next param as style)
46401      * @param {String} style the style property - eg. text-align
46402      */
46403     getVal : function(node, tag, attr, style)
46404     {
46405         var n = node;
46406         if (tag !== true && n.tagName != tag.toUpperCase()) {
46407             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46408             // but kiss for now.
46409             n = node.getElementsByTagName(tag).item(0);
46410         }
46411         if (attr == 'html') {
46412             return n.innerHTML;
46413         }
46414         if (attr == 'style') {
46415             return n.style[style]
46416         }
46417         
46418         return Roo.get(n).attr(attr);
46419             
46420     },
46421     /**
46422      * create a DomHelper friendly object - for use with 
46423      * Roo.DomHelper.markup / overwrite / etc..
46424      * (override this)
46425      */
46426     toObject : function()
46427     {
46428         return {};
46429     },
46430       /**
46431      * Read a node that has a 'data-block' property - and extract the values from it.
46432      * @param {DomElement} node - the node
46433      */
46434     readElement : function(node)
46435     {
46436         
46437     } 
46438     
46439     
46440 };
46441
46442  
46443
46444 /**
46445  * @class Roo.htmleditor.BlockFigure
46446  * Block that has an image and a figcaption
46447  * @cfg {String} image_src the url for the image
46448  * @cfg {String} align (left|right) alignment for the block default left
46449  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46450  * @cfg {String} caption the text to appear below  (and in the alt tag)
46451  * @cfg {String|number} image_width the width of the image number or %?
46452  * @cfg {String|number} image_height the height of the image number or %?
46453  * 
46454  * @constructor
46455  * Create a new Filter.
46456  * @param {Object} config Configuration options
46457  */
46458
46459 Roo.htmleditor.BlockFigure = function(cfg)
46460 {
46461     if (cfg.node) {
46462         this.readElement(cfg.node);
46463         this.updateElement(cfg.node);
46464     }
46465     Roo.apply(this, cfg);
46466 }
46467 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46468  
46469     
46470     // setable values.
46471     image_src: '',
46472     
46473     align: 'left',
46474     caption : '',
46475     text_align: 'left',
46476     
46477     width : '46%',
46478     margin: '2%',
46479     
46480     // used by context menu
46481     friendly_name : 'Image with caption',
46482     
46483     context : { // ?? static really
46484         width : {
46485             title: "Width",
46486             width: 40
46487             // ?? number
46488         },
46489         margin : {
46490             title: "Margin",
46491             width: 40
46492             // ?? number
46493         },
46494         align: {
46495             title: "Align",
46496             opts : [[ "left"],[ "right"]],
46497             width : 80
46498             
46499         },
46500         text_align: {
46501             title: "Caption Align",
46502             opts : [ [ "left"],[ "right"],[ "center"]],
46503             width : 80
46504         },
46505         
46506        
46507         image_src : {
46508             title: "Src",
46509             width: 220
46510         }
46511     },
46512     /**
46513      * create a DomHelper friendly object - for use with
46514      * Roo.DomHelper.markup / overwrite / etc..
46515      */
46516     toObject : function()
46517     {
46518         var d = document.createElement('div');
46519         d.innerHTML = this.caption;
46520         
46521         return {
46522             tag: 'figure',
46523             'data-block' : 'Figure',
46524             contenteditable : 'false',
46525             style : {
46526                 display: 'table',
46527                 float :  this.align ,
46528                 width :  this.width,
46529                 margin:  this.margin
46530             },
46531             cn : [
46532                 {
46533                     tag : 'img',
46534                     src : this.image_src,
46535                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46536                     style: {
46537                         width: '100%'
46538                     }
46539                 },
46540                 {
46541                     tag: 'figcaption',
46542                     contenteditable : true,
46543                     style : {
46544                         'text-align': this.text_align
46545                     },
46546                     html : this.caption
46547                     
46548                 }
46549             ]
46550         };
46551     },
46552     
46553     readElement : function(node)
46554     {
46555         this.image_src = this.getVal(node, 'img', 'src');
46556         this.align = this.getVal(node, 'figure', 'style', 'float');
46557         this.caption = this.getVal(node, 'figcaption', 'html');
46558         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46559         this.width = this.getVal(node, 'figure', 'style', 'width');
46560         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46561         
46562     } 
46563     
46564   
46565    
46566      
46567     
46568     
46569     
46570     
46571 })
46572
46573 //<script type="text/javascript">
46574
46575 /*
46576  * Based  Ext JS Library 1.1.1
46577  * Copyright(c) 2006-2007, Ext JS, LLC.
46578  * LGPL
46579  *
46580  */
46581  
46582 /**
46583  * @class Roo.HtmlEditorCore
46584  * @extends Roo.Component
46585  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46586  *
46587  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46588  */
46589
46590 Roo.HtmlEditorCore = function(config){
46591     
46592     
46593     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46594     
46595     
46596     this.addEvents({
46597         /**
46598          * @event initialize
46599          * Fires when the editor is fully initialized (including the iframe)
46600          * @param {Roo.HtmlEditorCore} this
46601          */
46602         initialize: true,
46603         /**
46604          * @event activate
46605          * Fires when the editor is first receives the focus. Any insertion must wait
46606          * until after this event.
46607          * @param {Roo.HtmlEditorCore} this
46608          */
46609         activate: true,
46610          /**
46611          * @event beforesync
46612          * Fires before the textarea is updated with content from the editor iframe. Return false
46613          * to cancel the sync.
46614          * @param {Roo.HtmlEditorCore} this
46615          * @param {String} html
46616          */
46617         beforesync: true,
46618          /**
46619          * @event beforepush
46620          * Fires before the iframe editor is updated with content from the textarea. Return false
46621          * to cancel the push.
46622          * @param {Roo.HtmlEditorCore} this
46623          * @param {String} html
46624          */
46625         beforepush: true,
46626          /**
46627          * @event sync
46628          * Fires when the textarea is updated with content from the editor iframe.
46629          * @param {Roo.HtmlEditorCore} this
46630          * @param {String} html
46631          */
46632         sync: true,
46633          /**
46634          * @event push
46635          * Fires when the iframe editor is updated with content from the textarea.
46636          * @param {Roo.HtmlEditorCore} this
46637          * @param {String} html
46638          */
46639         push: true,
46640         
46641         /**
46642          * @event editorevent
46643          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46644          * @param {Roo.HtmlEditorCore} this
46645          */
46646         editorevent: true
46647         
46648     });
46649     
46650     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46651     
46652     // defaults : white / black...
46653     this.applyBlacklists();
46654     
46655     
46656     
46657 };
46658
46659
46660 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46661
46662
46663      /**
46664      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46665      */
46666     
46667     owner : false,
46668     
46669      /**
46670      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46671      *                        Roo.resizable.
46672      */
46673     resizable : false,
46674      /**
46675      * @cfg {Number} height (in pixels)
46676      */   
46677     height: 300,
46678    /**
46679      * @cfg {Number} width (in pixels)
46680      */   
46681     width: 500,
46682     
46683     /**
46684      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46685      * 
46686      */
46687     stylesheets: false,
46688     
46689     /**
46690      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46691      */
46692     allowComments: false,
46693     // id of frame..
46694     frameId: false,
46695     
46696     // private properties
46697     validationEvent : false,
46698     deferHeight: true,
46699     initialized : false,
46700     activated : false,
46701     sourceEditMode : false,
46702     onFocus : Roo.emptyFn,
46703     iframePad:3,
46704     hideMode:'offsets',
46705     
46706     clearUp: true,
46707     
46708     // blacklist + whitelisted elements..
46709     black: false,
46710     white: false,
46711      
46712     bodyCls : '',
46713
46714     
46715     undoManager : false,
46716     /**
46717      * Protected method that will not generally be called directly. It
46718      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46719      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46720      */
46721     getDocMarkup : function(){
46722         // body styles..
46723         var st = '';
46724         
46725         // inherit styels from page...?? 
46726         if (this.stylesheets === false) {
46727             
46728             Roo.get(document.head).select('style').each(function(node) {
46729                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46730             });
46731             
46732             Roo.get(document.head).select('link').each(function(node) { 
46733                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46734             });
46735             
46736         } else if (!this.stylesheets.length) {
46737                 // simple..
46738                 st = '<style type="text/css">' +
46739                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46740                    '</style>';
46741         } else {
46742             for (var i in this.stylesheets) {
46743                 if (typeof(this.stylesheets[i]) != 'string') {
46744                     continue;
46745                 }
46746                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46747             }
46748             
46749         }
46750         
46751         st +=  '<style type="text/css">' +
46752             'IMG { cursor: pointer } ' +
46753         '</style>';
46754
46755         var cls = 'roo-htmleditor-body';
46756         
46757         if(this.bodyCls.length){
46758             cls += ' ' + this.bodyCls;
46759         }
46760         
46761         return '<html><head>' + st  +
46762             //<style type="text/css">' +
46763             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46764             //'</style>' +
46765             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46766     },
46767
46768     // private
46769     onRender : function(ct, position)
46770     {
46771         var _t = this;
46772         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46773         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46774         
46775         
46776         this.el.dom.style.border = '0 none';
46777         this.el.dom.setAttribute('tabIndex', -1);
46778         this.el.addClass('x-hidden hide');
46779         
46780         
46781         
46782         if(Roo.isIE){ // fix IE 1px bogus margin
46783             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46784         }
46785        
46786         
46787         this.frameId = Roo.id();
46788         
46789          
46790         
46791         var iframe = this.owner.wrap.createChild({
46792             tag: 'iframe',
46793             cls: 'form-control', // bootstrap..
46794             id: this.frameId,
46795             name: this.frameId,
46796             frameBorder : 'no',
46797             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46798         }, this.el
46799         );
46800         
46801         
46802         this.iframe = iframe.dom;
46803
46804         this.assignDocWin();
46805         
46806         this.doc.designMode = 'on';
46807        
46808         this.doc.open();
46809         this.doc.write(this.getDocMarkup());
46810         this.doc.close();
46811
46812         
46813         var task = { // must defer to wait for browser to be ready
46814             run : function(){
46815                 //console.log("run task?" + this.doc.readyState);
46816                 this.assignDocWin();
46817                 if(this.doc.body || this.doc.readyState == 'complete'){
46818                     try {
46819                         this.doc.designMode="on";
46820                         
46821                     } catch (e) {
46822                         return;
46823                     }
46824                     Roo.TaskMgr.stop(task);
46825                     this.initEditor.defer(10, this);
46826                 }
46827             },
46828             interval : 10,
46829             duration: 10000,
46830             scope: this
46831         };
46832         Roo.TaskMgr.start(task);
46833
46834     },
46835
46836     // private
46837     onResize : function(w, h)
46838     {
46839          Roo.log('resize: ' +w + ',' + h );
46840         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46841         if(!this.iframe){
46842             return;
46843         }
46844         if(typeof w == 'number'){
46845             
46846             this.iframe.style.width = w + 'px';
46847         }
46848         if(typeof h == 'number'){
46849             
46850             this.iframe.style.height = h + 'px';
46851             if(this.doc){
46852                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46853             }
46854         }
46855         
46856     },
46857
46858     /**
46859      * Toggles the editor between standard and source edit mode.
46860      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46861      */
46862     toggleSourceEdit : function(sourceEditMode){
46863         
46864         this.sourceEditMode = sourceEditMode === true;
46865         
46866         if(this.sourceEditMode){
46867  
46868             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46869             
46870         }else{
46871             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46872             //this.iframe.className = '';
46873             this.deferFocus();
46874         }
46875         //this.setSize(this.owner.wrap.getSize());
46876         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46877     },
46878
46879     
46880   
46881
46882     /**
46883      * Protected method that will not generally be called directly. If you need/want
46884      * custom HTML cleanup, this is the method you should override.
46885      * @param {String} html The HTML to be cleaned
46886      * return {String} The cleaned HTML
46887      */
46888     cleanHtml : function(html){
46889         html = String(html);
46890         if(html.length > 5){
46891             if(Roo.isSafari){ // strip safari nonsense
46892                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46893             }
46894         }
46895         if(html == '&nbsp;'){
46896             html = '';
46897         }
46898         return html;
46899     },
46900
46901     /**
46902      * HTML Editor -> Textarea
46903      * Protected method that will not generally be called directly. Syncs the contents
46904      * of the editor iframe with the textarea.
46905      */
46906     syncValue : function()
46907     {
46908         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46909         if(this.initialized){
46910             
46911             this.undoManager.addEvent();
46912
46913             
46914             var bd = (this.doc.body || this.doc.documentElement);
46915             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46916             
46917             // not sure if this is really the place for this
46918             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46919             // this has to update attributes that get duped.. like alt and caption..
46920             
46921             
46922             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46923             //     Roo.htmleditor.Block.factory(e);
46924             //},this);
46925             
46926             
46927             var div = document.createElement('div');
46928             div.innerHTML = bd.innerHTML;
46929             // remove content editable. (blocks)
46930             
46931            
46932             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46933             //?? tidy?
46934             var html = div.innerHTML;
46935             if(Roo.isSafari){
46936                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46937                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46938                 if(m && m[1]){
46939                     html = '<div style="'+m[0]+'">' + html + '</div>';
46940                 }
46941             }
46942             html = this.cleanHtml(html);
46943             // fix up the special chars.. normaly like back quotes in word...
46944             // however we do not want to do this with chinese..
46945             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46946                 
46947                 var cc = match.charCodeAt();
46948
46949                 // Get the character value, handling surrogate pairs
46950                 if (match.length == 2) {
46951                     // It's a surrogate pair, calculate the Unicode code point
46952                     var high = match.charCodeAt(0) - 0xD800;
46953                     var low  = match.charCodeAt(1) - 0xDC00;
46954                     cc = (high * 0x400) + low + 0x10000;
46955                 }  else if (
46956                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46957                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46958                     (cc >= 0xf900 && cc < 0xfb00 )
46959                 ) {
46960                         return match;
46961                 }  
46962          
46963                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46964                 return "&#" + cc + ";";
46965                 
46966                 
46967             });
46968             
46969             
46970              
46971             if(this.owner.fireEvent('beforesync', this, html) !== false){
46972                 this.el.dom.value = html;
46973                 this.owner.fireEvent('sync', this, html);
46974             }
46975         }
46976     },
46977
46978     /**
46979      * TEXTAREA -> EDITABLE
46980      * Protected method that will not generally be called directly. Pushes the value of the textarea
46981      * into the iframe editor.
46982      */
46983     pushValue : function()
46984     {
46985         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46986         if(this.initialized){
46987             var v = this.el.dom.value.trim();
46988             
46989             
46990             if(this.owner.fireEvent('beforepush', this, v) !== false){
46991                 var d = (this.doc.body || this.doc.documentElement);
46992                 d.innerHTML = v;
46993                  
46994                 this.el.dom.value = d.innerHTML;
46995                 this.owner.fireEvent('push', this, v);
46996             }
46997             
46998             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46999                 
47000                 Roo.htmleditor.Block.factory(e);
47001                 
47002             },this);
47003             var lc = this.doc.body.lastChild;
47004             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
47005                 // add an extra line at the end.
47006                 this.doc.body.appendChild(this.doc.createElement('br'));
47007             }
47008             
47009             
47010         }
47011     },
47012
47013     // private
47014     deferFocus : function(){
47015         this.focus.defer(10, this);
47016     },
47017
47018     // doc'ed in Field
47019     focus : function(){
47020         if(this.win && !this.sourceEditMode){
47021             this.win.focus();
47022         }else{
47023             this.el.focus();
47024         }
47025     },
47026     
47027     assignDocWin: function()
47028     {
47029         var iframe = this.iframe;
47030         
47031          if(Roo.isIE){
47032             this.doc = iframe.contentWindow.document;
47033             this.win = iframe.contentWindow;
47034         } else {
47035 //            if (!Roo.get(this.frameId)) {
47036 //                return;
47037 //            }
47038 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47039 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47040             
47041             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47042                 return;
47043             }
47044             
47045             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47046             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47047         }
47048     },
47049     
47050     // private
47051     initEditor : function(){
47052         //console.log("INIT EDITOR");
47053         this.assignDocWin();
47054         
47055         
47056         
47057         this.doc.designMode="on";
47058         this.doc.open();
47059         this.doc.write(this.getDocMarkup());
47060         this.doc.close();
47061         
47062         var dbody = (this.doc.body || this.doc.documentElement);
47063         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47064         // this copies styles from the containing element into thsi one..
47065         // not sure why we need all of this..
47066         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47067         
47068         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47069         //ss['background-attachment'] = 'fixed'; // w3c
47070         dbody.bgProperties = 'fixed'; // ie
47071         //Roo.DomHelper.applyStyles(dbody, ss);
47072         Roo.EventManager.on(this.doc, {
47073             //'mousedown': this.onEditorEvent,
47074             'mouseup': this.onEditorEvent,
47075             'dblclick': this.onEditorEvent,
47076             'click': this.onEditorEvent,
47077             'keyup': this.onEditorEvent,
47078             
47079             buffer:100,
47080             scope: this
47081         });
47082         Roo.EventManager.on(this.doc, {
47083             'paste': this.onPasteEvent,
47084             scope : this
47085         });
47086         if(Roo.isGecko){
47087             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47088         }
47089         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47090             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47091         }
47092         this.initialized = true;
47093
47094         
47095         // initialize special key events - enter
47096         new Roo.htmleditor.KeyEnter({core : this});
47097         
47098          
47099         
47100         this.owner.fireEvent('initialize', this);
47101         this.pushValue();
47102     },
47103     
47104     onPasteEvent : function(e,v)
47105     {
47106         // I think we better assume paste is going to be a dirty load of rubish from word..
47107         
47108         // even pasting into a 'email version' of this widget will have to clean up that mess.
47109         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47110         
47111         // check what type of paste - if it's an image, then handle it differently.
47112         if (cd.files.length > 0) {
47113             // pasting images?
47114             var urlAPI = (window.createObjectURL && window) || 
47115                 (window.URL && URL.revokeObjectURL && URL) || 
47116                 (window.webkitURL && webkitURL);
47117     
47118             var url = urlAPI.createObjectURL( cd.files[0]);
47119             this.insertAtCursor('<img src=" + url + ">');
47120             return false;
47121         }
47122         
47123         var html = cd.getData('text/html'); // clipboard event
47124         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47125         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47126         Roo.log(images);
47127         //Roo.log(imgs);
47128         // fixme..
47129         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47130                        .map(function(g) { return g.toDataURL(); });
47131         
47132         
47133         html = this.cleanWordChars(html);
47134         
47135         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47136         
47137         if (images.length > 0) {
47138             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47139                 img.setAttribute('src', images[i]);
47140             });
47141         }
47142         
47143       
47144         new Roo.htmleditor.FilterStyleToTag({ node : d });
47145         new Roo.htmleditor.FilterAttributes({
47146             node : d,
47147             attrib_white : ['href', 'src', 'name', 'align'],
47148             attrib_clean : ['href', 'src' ] 
47149         });
47150         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47151         // should be fonts..
47152         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47153         new Roo.htmleditor.FilterParagraph({ node : d });
47154         new Roo.htmleditor.FilterSpan({ node : d });
47155         new Roo.htmleditor.FilterLongBr({ node : d });
47156         
47157         
47158         
47159         this.insertAtCursor(d.innerHTML);
47160         
47161         e.preventDefault();
47162         return false;
47163         // default behaveiour should be our local cleanup paste? (optional?)
47164         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47165         //this.owner.fireEvent('paste', e, v);
47166     },
47167     // private
47168     onDestroy : function(){
47169         
47170         
47171         
47172         if(this.rendered){
47173             
47174             //for (var i =0; i < this.toolbars.length;i++) {
47175             //    // fixme - ask toolbars for heights?
47176             //    this.toolbars[i].onDestroy();
47177            // }
47178             
47179             //this.wrap.dom.innerHTML = '';
47180             //this.wrap.remove();
47181         }
47182     },
47183
47184     // private
47185     onFirstFocus : function(){
47186         
47187         this.assignDocWin();
47188         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47189         
47190         this.activated = true;
47191          
47192     
47193         if(Roo.isGecko){ // prevent silly gecko errors
47194             this.win.focus();
47195             var s = this.win.getSelection();
47196             if(!s.focusNode || s.focusNode.nodeType != 3){
47197                 var r = s.getRangeAt(0);
47198                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47199                 r.collapse(true);
47200                 this.deferFocus();
47201             }
47202             try{
47203                 this.execCmd('useCSS', true);
47204                 this.execCmd('styleWithCSS', false);
47205             }catch(e){}
47206         }
47207         this.owner.fireEvent('activate', this);
47208     },
47209
47210     // private
47211     adjustFont: function(btn){
47212         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47213         //if(Roo.isSafari){ // safari
47214         //    adjust *= 2;
47215        // }
47216         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47217         if(Roo.isSafari){ // safari
47218             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47219             v =  (v < 10) ? 10 : v;
47220             v =  (v > 48) ? 48 : v;
47221             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47222             
47223         }
47224         
47225         
47226         v = Math.max(1, v+adjust);
47227         
47228         this.execCmd('FontSize', v  );
47229     },
47230
47231     onEditorEvent : function(e)
47232     {
47233         this.owner.fireEvent('editorevent', this, e);
47234       //  this.updateToolbar();
47235         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47236     },
47237
47238     insertTag : function(tg)
47239     {
47240         // could be a bit smarter... -> wrap the current selected tRoo..
47241         if (tg.toLowerCase() == 'span' ||
47242             tg.toLowerCase() == 'code' ||
47243             tg.toLowerCase() == 'sup' ||
47244             tg.toLowerCase() == 'sub' 
47245             ) {
47246             
47247             range = this.createRange(this.getSelection());
47248             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47249             wrappingNode.appendChild(range.extractContents());
47250             range.insertNode(wrappingNode);
47251
47252             return;
47253             
47254             
47255             
47256         }
47257         this.execCmd("formatblock",   tg);
47258         this.undoManager.addEvent(); 
47259     },
47260     
47261     insertText : function(txt)
47262     {
47263         
47264         
47265         var range = this.createRange();
47266         range.deleteContents();
47267                //alert(Sender.getAttribute('label'));
47268                
47269         range.insertNode(this.doc.createTextNode(txt));
47270         this.undoManager.addEvent();
47271     } ,
47272     
47273      
47274
47275     /**
47276      * Executes a Midas editor command on the editor document and performs necessary focus and
47277      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47278      * @param {String} cmd The Midas command
47279      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47280      */
47281     relayCmd : function(cmd, value){
47282         this.win.focus();
47283         this.execCmd(cmd, value);
47284         this.owner.fireEvent('editorevent', this);
47285         //this.updateToolbar();
47286         this.owner.deferFocus();
47287     },
47288
47289     /**
47290      * Executes a Midas editor command directly on the editor document.
47291      * For visual commands, you should use {@link #relayCmd} instead.
47292      * <b>This should only be called after the editor is initialized.</b>
47293      * @param {String} cmd The Midas command
47294      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47295      */
47296     execCmd : function(cmd, value){
47297         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47298         this.syncValue();
47299     },
47300  
47301  
47302    
47303     /**
47304      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47305      * to insert tRoo.
47306      * @param {String} text | dom node.. 
47307      */
47308     insertAtCursor : function(text)
47309     {
47310         
47311         if(!this.activated){
47312             return;
47313         }
47314          
47315         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47316             this.win.focus();
47317             
47318             
47319             // from jquery ui (MIT licenced)
47320             var range, node;
47321             var win = this.win;
47322             
47323             if (win.getSelection && win.getSelection().getRangeAt) {
47324                 
47325                 // delete the existing?
47326                 
47327                 this.createRange(this.getSelection()).deleteContents();
47328                 range = win.getSelection().getRangeAt(0);
47329                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47330                 range.insertNode(node);
47331                 range = range.cloneRange();
47332                 range.collapse(false);
47333                  
47334                 win.getSelection().removeAllRanges();
47335                 win.getSelection().addRange(range);
47336                 
47337                 
47338                 
47339             } else if (win.document.selection && win.document.selection.createRange) {
47340                 // no firefox support
47341                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47342                 win.document.selection.createRange().pasteHTML(txt);
47343             
47344             } else {
47345                 // no firefox support
47346                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47347                 this.execCmd('InsertHTML', txt);
47348             } 
47349             this.syncValue();
47350             
47351             this.deferFocus();
47352         }
47353     },
47354  // private
47355     mozKeyPress : function(e){
47356         if(e.ctrlKey){
47357             var c = e.getCharCode(), cmd;
47358           
47359             if(c > 0){
47360                 c = String.fromCharCode(c).toLowerCase();
47361                 switch(c){
47362                     case 'b':
47363                         cmd = 'bold';
47364                         break;
47365                     case 'i':
47366                         cmd = 'italic';
47367                         break;
47368                     
47369                     case 'u':
47370                         cmd = 'underline';
47371                         break;
47372                     
47373                     //case 'v':
47374                       //  this.cleanUpPaste.defer(100, this);
47375                       //  return;
47376                         
47377                 }
47378                 if(cmd){
47379                     this.win.focus();
47380                     this.execCmd(cmd);
47381                     this.deferFocus();
47382                     e.preventDefault();
47383                 }
47384                 
47385             }
47386         }
47387     },
47388
47389     // private
47390     fixKeys : function(){ // load time branching for fastest keydown performance
47391         if(Roo.isIE){
47392             return function(e){
47393                 var k = e.getKey(), r;
47394                 if(k == e.TAB){
47395                     e.stopEvent();
47396                     r = this.doc.selection.createRange();
47397                     if(r){
47398                         r.collapse(true);
47399                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47400                         this.deferFocus();
47401                     }
47402                     return;
47403                 }
47404                 
47405                 if(k == e.ENTER){
47406                     r = this.doc.selection.createRange();
47407                     if(r){
47408                         var target = r.parentElement();
47409                         if(!target || target.tagName.toLowerCase() != 'li'){
47410                             e.stopEvent();
47411                             r.pasteHTML('<br/>');
47412                             r.collapse(false);
47413                             r.select();
47414                         }
47415                     }
47416                 }
47417                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47418                 //    this.cleanUpPaste.defer(100, this);
47419                 //    return;
47420                 //}
47421                 
47422                 
47423             };
47424         }else if(Roo.isOpera){
47425             return function(e){
47426                 var k = e.getKey();
47427                 if(k == e.TAB){
47428                     e.stopEvent();
47429                     this.win.focus();
47430                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47431                     this.deferFocus();
47432                 }
47433                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47434                 //    this.cleanUpPaste.defer(100, this);
47435                  //   return;
47436                 //}
47437                 
47438             };
47439         }else if(Roo.isSafari){
47440             return function(e){
47441                 var k = e.getKey();
47442                 
47443                 if(k == e.TAB){
47444                     e.stopEvent();
47445                     this.execCmd('InsertText','\t');
47446                     this.deferFocus();
47447                     return;
47448                 }
47449                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47450                  //   this.cleanUpPaste.defer(100, this);
47451                  //   return;
47452                // }
47453                 
47454              };
47455         }
47456     }(),
47457     
47458     getAllAncestors: function()
47459     {
47460         var p = this.getSelectedNode();
47461         var a = [];
47462         if (!p) {
47463             a.push(p); // push blank onto stack..
47464             p = this.getParentElement();
47465         }
47466         
47467         
47468         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47469             a.push(p);
47470             p = p.parentNode;
47471         }
47472         a.push(this.doc.body);
47473         return a;
47474     },
47475     lastSel : false,
47476     lastSelNode : false,
47477     
47478     
47479     getSelection : function() 
47480     {
47481         this.assignDocWin();
47482         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47483     },
47484     /**
47485      * Select a dom node
47486      * @param {DomElement} node the node to select
47487      */
47488     selectNode : function(node)
47489     {
47490         var nodeRange = node.ownerDocument.createRange();
47491         try {
47492             nodeRange.selectNode(node);
47493         } catch (e) {
47494             nodeRange.selectNodeContents(node);
47495         }
47496         //nodeRange.collapse(true);
47497         var s = this.win.getSelection();
47498         s.removeAllRanges();
47499         s.addRange(nodeRange);
47500     },
47501     
47502     getSelectedNode: function() 
47503     {
47504         // this may only work on Gecko!!!
47505         
47506         // should we cache this!!!!
47507         
47508         
47509         
47510          
47511         var range = this.createRange(this.getSelection()).cloneRange();
47512         
47513         if (Roo.isIE) {
47514             var parent = range.parentElement();
47515             while (true) {
47516                 var testRange = range.duplicate();
47517                 testRange.moveToElementText(parent);
47518                 if (testRange.inRange(range)) {
47519                     break;
47520                 }
47521                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47522                     break;
47523                 }
47524                 parent = parent.parentElement;
47525             }
47526             return parent;
47527         }
47528         
47529         // is ancestor a text element.
47530         var ac =  range.commonAncestorContainer;
47531         if (ac.nodeType == 3) {
47532             ac = ac.parentNode;
47533         }
47534         
47535         var ar = ac.childNodes;
47536          
47537         var nodes = [];
47538         var other_nodes = [];
47539         var has_other_nodes = false;
47540         for (var i=0;i<ar.length;i++) {
47541             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47542                 continue;
47543             }
47544             // fullly contained node.
47545             
47546             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47547                 nodes.push(ar[i]);
47548                 continue;
47549             }
47550             
47551             // probably selected..
47552             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47553                 other_nodes.push(ar[i]);
47554                 continue;
47555             }
47556             // outer..
47557             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47558                 continue;
47559             }
47560             
47561             
47562             has_other_nodes = true;
47563         }
47564         if (!nodes.length && other_nodes.length) {
47565             nodes= other_nodes;
47566         }
47567         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47568             return false;
47569         }
47570         
47571         return nodes[0];
47572     },
47573     createRange: function(sel)
47574     {
47575         // this has strange effects when using with 
47576         // top toolbar - not sure if it's a great idea.
47577         //this.editor.contentWindow.focus();
47578         if (typeof sel != "undefined") {
47579             try {
47580                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47581             } catch(e) {
47582                 return this.doc.createRange();
47583             }
47584         } else {
47585             return this.doc.createRange();
47586         }
47587     },
47588     getParentElement: function()
47589     {
47590         
47591         this.assignDocWin();
47592         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47593         
47594         var range = this.createRange(sel);
47595          
47596         try {
47597             var p = range.commonAncestorContainer;
47598             while (p.nodeType == 3) { // text node
47599                 p = p.parentNode;
47600             }
47601             return p;
47602         } catch (e) {
47603             return null;
47604         }
47605     
47606     },
47607     /***
47608      *
47609      * Range intersection.. the hard stuff...
47610      *  '-1' = before
47611      *  '0' = hits..
47612      *  '1' = after.
47613      *         [ -- selected range --- ]
47614      *   [fail]                        [fail]
47615      *
47616      *    basically..
47617      *      if end is before start or  hits it. fail.
47618      *      if start is after end or hits it fail.
47619      *
47620      *   if either hits (but other is outside. - then it's not 
47621      *   
47622      *    
47623      **/
47624     
47625     
47626     // @see http://www.thismuchiknow.co.uk/?p=64.
47627     rangeIntersectsNode : function(range, node)
47628     {
47629         var nodeRange = node.ownerDocument.createRange();
47630         try {
47631             nodeRange.selectNode(node);
47632         } catch (e) {
47633             nodeRange.selectNodeContents(node);
47634         }
47635     
47636         var rangeStartRange = range.cloneRange();
47637         rangeStartRange.collapse(true);
47638     
47639         var rangeEndRange = range.cloneRange();
47640         rangeEndRange.collapse(false);
47641     
47642         var nodeStartRange = nodeRange.cloneRange();
47643         nodeStartRange.collapse(true);
47644     
47645         var nodeEndRange = nodeRange.cloneRange();
47646         nodeEndRange.collapse(false);
47647     
47648         return rangeStartRange.compareBoundaryPoints(
47649                  Range.START_TO_START, nodeEndRange) == -1 &&
47650                rangeEndRange.compareBoundaryPoints(
47651                  Range.START_TO_START, nodeStartRange) == 1;
47652         
47653          
47654     },
47655     rangeCompareNode : function(range, node)
47656     {
47657         var nodeRange = node.ownerDocument.createRange();
47658         try {
47659             nodeRange.selectNode(node);
47660         } catch (e) {
47661             nodeRange.selectNodeContents(node);
47662         }
47663         
47664         
47665         range.collapse(true);
47666     
47667         nodeRange.collapse(true);
47668      
47669         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47670         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47671          
47672         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47673         
47674         var nodeIsBefore   =  ss == 1;
47675         var nodeIsAfter    = ee == -1;
47676         
47677         if (nodeIsBefore && nodeIsAfter) {
47678             return 0; // outer
47679         }
47680         if (!nodeIsBefore && nodeIsAfter) {
47681             return 1; //right trailed.
47682         }
47683         
47684         if (nodeIsBefore && !nodeIsAfter) {
47685             return 2;  // left trailed.
47686         }
47687         // fully contined.
47688         return 3;
47689     },
47690  
47691     cleanWordChars : function(input) {// change the chars to hex code
47692         
47693        var swapCodes  = [ 
47694             [    8211, "&#8211;" ], 
47695             [    8212, "&#8212;" ], 
47696             [    8216,  "'" ],  
47697             [    8217, "'" ],  
47698             [    8220, '"' ],  
47699             [    8221, '"' ],  
47700             [    8226, "*" ],  
47701             [    8230, "..." ]
47702         ]; 
47703         var output = input;
47704         Roo.each(swapCodes, function(sw) { 
47705             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47706             
47707             output = output.replace(swapper, sw[1]);
47708         });
47709         
47710         return output;
47711     },
47712     
47713      
47714     
47715         
47716     
47717     cleanUpChild : function (node)
47718     {
47719         
47720         new Roo.htmleditor.FilterComment({node : node});
47721         new Roo.htmleditor.FilterAttributes({
47722                 node : node,
47723                 attrib_black : this.ablack,
47724                 attrib_clean : this.aclean,
47725                 style_white : this.cwhite,
47726                 style_black : this.cblack
47727         });
47728         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47729         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47730          
47731         
47732     },
47733     
47734     /**
47735      * Clean up MS wordisms...
47736      * @deprecated - use filter directly
47737      */
47738     cleanWord : function(node)
47739     {
47740         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47741         
47742     },
47743    
47744     
47745     /**
47746
47747      * @deprecated - use filters
47748      */
47749     cleanTableWidths : function(node)
47750     {
47751         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47752         
47753  
47754     },
47755     
47756      
47757         
47758     applyBlacklists : function()
47759     {
47760         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47761         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47762         
47763         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47764         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47765         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47766         
47767         this.white = [];
47768         this.black = [];
47769         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47770             if (b.indexOf(tag) > -1) {
47771                 return;
47772             }
47773             this.white.push(tag);
47774             
47775         }, this);
47776         
47777         Roo.each(w, function(tag) {
47778             if (b.indexOf(tag) > -1) {
47779                 return;
47780             }
47781             if (this.white.indexOf(tag) > -1) {
47782                 return;
47783             }
47784             this.white.push(tag);
47785             
47786         }, this);
47787         
47788         
47789         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47790             if (w.indexOf(tag) > -1) {
47791                 return;
47792             }
47793             this.black.push(tag);
47794             
47795         }, this);
47796         
47797         Roo.each(b, function(tag) {
47798             if (w.indexOf(tag) > -1) {
47799                 return;
47800             }
47801             if (this.black.indexOf(tag) > -1) {
47802                 return;
47803             }
47804             this.black.push(tag);
47805             
47806         }, this);
47807         
47808         
47809         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47810         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47811         
47812         this.cwhite = [];
47813         this.cblack = [];
47814         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47815             if (b.indexOf(tag) > -1) {
47816                 return;
47817             }
47818             this.cwhite.push(tag);
47819             
47820         }, this);
47821         
47822         Roo.each(w, function(tag) {
47823             if (b.indexOf(tag) > -1) {
47824                 return;
47825             }
47826             if (this.cwhite.indexOf(tag) > -1) {
47827                 return;
47828             }
47829             this.cwhite.push(tag);
47830             
47831         }, this);
47832         
47833         
47834         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47835             if (w.indexOf(tag) > -1) {
47836                 return;
47837             }
47838             this.cblack.push(tag);
47839             
47840         }, this);
47841         
47842         Roo.each(b, function(tag) {
47843             if (w.indexOf(tag) > -1) {
47844                 return;
47845             }
47846             if (this.cblack.indexOf(tag) > -1) {
47847                 return;
47848             }
47849             this.cblack.push(tag);
47850             
47851         }, this);
47852     },
47853     
47854     setStylesheets : function(stylesheets)
47855     {
47856         if(typeof(stylesheets) == 'string'){
47857             Roo.get(this.iframe.contentDocument.head).createChild({
47858                 tag : 'link',
47859                 rel : 'stylesheet',
47860                 type : 'text/css',
47861                 href : stylesheets
47862             });
47863             
47864             return;
47865         }
47866         var _this = this;
47867      
47868         Roo.each(stylesheets, function(s) {
47869             if(!s.length){
47870                 return;
47871             }
47872             
47873             Roo.get(_this.iframe.contentDocument.head).createChild({
47874                 tag : 'link',
47875                 rel : 'stylesheet',
47876                 type : 'text/css',
47877                 href : s
47878             });
47879         });
47880
47881         
47882     },
47883     
47884     removeStylesheets : function()
47885     {
47886         var _this = this;
47887         
47888         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47889             s.remove();
47890         });
47891     },
47892     
47893     setStyle : function(style)
47894     {
47895         Roo.get(this.iframe.contentDocument.head).createChild({
47896             tag : 'style',
47897             type : 'text/css',
47898             html : style
47899         });
47900
47901         return;
47902     }
47903     
47904     // hide stuff that is not compatible
47905     /**
47906      * @event blur
47907      * @hide
47908      */
47909     /**
47910      * @event change
47911      * @hide
47912      */
47913     /**
47914      * @event focus
47915      * @hide
47916      */
47917     /**
47918      * @event specialkey
47919      * @hide
47920      */
47921     /**
47922      * @cfg {String} fieldClass @hide
47923      */
47924     /**
47925      * @cfg {String} focusClass @hide
47926      */
47927     /**
47928      * @cfg {String} autoCreate @hide
47929      */
47930     /**
47931      * @cfg {String} inputType @hide
47932      */
47933     /**
47934      * @cfg {String} invalidClass @hide
47935      */
47936     /**
47937      * @cfg {String} invalidText @hide
47938      */
47939     /**
47940      * @cfg {String} msgFx @hide
47941      */
47942     /**
47943      * @cfg {String} validateOnBlur @hide
47944      */
47945 });
47946
47947 Roo.HtmlEditorCore.white = [
47948         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47949         
47950        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47951        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47952        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47953        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47954        'TABLE',   'UL',         'XMP', 
47955        
47956        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47957       'THEAD',   'TR', 
47958      
47959       'DIR', 'MENU', 'OL', 'UL', 'DL',
47960        
47961       'EMBED',  'OBJECT'
47962 ];
47963
47964
47965 Roo.HtmlEditorCore.black = [
47966     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47967         'APPLET', // 
47968         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47969         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47970         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47971         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47972         //'FONT' // CLEAN LATER..
47973         'COLGROUP', 'COL'  // messy tables.
47974         
47975 ];
47976 Roo.HtmlEditorCore.clean = [ // ?? needed???
47977      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47978 ];
47979 Roo.HtmlEditorCore.tag_remove = [
47980     'FONT', 'TBODY'  
47981 ];
47982 // attributes..
47983
47984 Roo.HtmlEditorCore.ablack = [
47985     'on'
47986 ];
47987     
47988 Roo.HtmlEditorCore.aclean = [ 
47989     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47990 ];
47991
47992 // protocols..
47993 Roo.HtmlEditorCore.pwhite= [
47994         'http',  'https',  'mailto'
47995 ];
47996
47997 // white listed style attributes.
47998 Roo.HtmlEditorCore.cwhite= [
47999       //  'text-align', /// default is to allow most things..
48000       
48001          
48002 //        'font-size'//??
48003 ];
48004
48005 // black listed style attributes.
48006 Roo.HtmlEditorCore.cblack= [
48007       //  'font-size' -- this can be set by the project 
48008 ];
48009
48010
48011
48012
48013     //<script type="text/javascript">
48014
48015 /*
48016  * Ext JS Library 1.1.1
48017  * Copyright(c) 2006-2007, Ext JS, LLC.
48018  * Licence LGPL
48019  * 
48020  */
48021  
48022  
48023 Roo.form.HtmlEditor = function(config){
48024     
48025     
48026     
48027     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48028     
48029     if (!this.toolbars) {
48030         this.toolbars = [];
48031     }
48032     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48033     
48034     
48035 };
48036
48037 /**
48038  * @class Roo.form.HtmlEditor
48039  * @extends Roo.form.Field
48040  * Provides a lightweight HTML Editor component.
48041  *
48042  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48043  * 
48044  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48045  * supported by this editor.</b><br/><br/>
48046  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48047  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48048  */
48049 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48050     /**
48051      * @cfg {Boolean} clearUp
48052      */
48053     clearUp : true,
48054       /**
48055      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48056      */
48057     toolbars : false,
48058    
48059      /**
48060      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48061      *                        Roo.resizable.
48062      */
48063     resizable : false,
48064      /**
48065      * @cfg {Number} height (in pixels)
48066      */   
48067     height: 300,
48068    /**
48069      * @cfg {Number} width (in pixels)
48070      */   
48071     width: 500,
48072     
48073     /**
48074      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48075      * 
48076      */
48077     stylesheets: false,
48078     
48079     
48080      /**
48081      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48082      * 
48083      */
48084     cblack: false,
48085     /**
48086      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48087      * 
48088      */
48089     cwhite: false,
48090     
48091      /**
48092      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48093      * 
48094      */
48095     black: false,
48096     /**
48097      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48098      * 
48099      */
48100     white: false,
48101     /**
48102      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48103      */
48104     allowComments: false,
48105     /**
48106      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48107      */
48108     
48109     
48110      bodyCls : '',
48111     
48112     // id of frame..
48113     frameId: false,
48114     
48115     // private properties
48116     validationEvent : false,
48117     deferHeight: true,
48118     initialized : false,
48119     activated : false,
48120     
48121     onFocus : Roo.emptyFn,
48122     iframePad:3,
48123     hideMode:'offsets',
48124     
48125     actionMode : 'container', // defaults to hiding it...
48126     
48127     defaultAutoCreate : { // modified by initCompnoent..
48128         tag: "textarea",
48129         style:"width:500px;height:300px;",
48130         autocomplete: "new-password"
48131     },
48132
48133     // private
48134     initComponent : function(){
48135         this.addEvents({
48136             /**
48137              * @event initialize
48138              * Fires when the editor is fully initialized (including the iframe)
48139              * @param {HtmlEditor} this
48140              */
48141             initialize: true,
48142             /**
48143              * @event activate
48144              * Fires when the editor is first receives the focus. Any insertion must wait
48145              * until after this event.
48146              * @param {HtmlEditor} this
48147              */
48148             activate: true,
48149              /**
48150              * @event beforesync
48151              * Fires before the textarea is updated with content from the editor iframe. Return false
48152              * to cancel the sync.
48153              * @param {HtmlEditor} this
48154              * @param {String} html
48155              */
48156             beforesync: true,
48157              /**
48158              * @event beforepush
48159              * Fires before the iframe editor is updated with content from the textarea. Return false
48160              * to cancel the push.
48161              * @param {HtmlEditor} this
48162              * @param {String} html
48163              */
48164             beforepush: true,
48165              /**
48166              * @event sync
48167              * Fires when the textarea is updated with content from the editor iframe.
48168              * @param {HtmlEditor} this
48169              * @param {String} html
48170              */
48171             sync: true,
48172              /**
48173              * @event push
48174              * Fires when the iframe editor is updated with content from the textarea.
48175              * @param {HtmlEditor} this
48176              * @param {String} html
48177              */
48178             push: true,
48179              /**
48180              * @event editmodechange
48181              * Fires when the editor switches edit modes
48182              * @param {HtmlEditor} this
48183              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48184              */
48185             editmodechange: true,
48186             /**
48187              * @event editorevent
48188              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48189              * @param {HtmlEditor} this
48190              */
48191             editorevent: true,
48192             /**
48193              * @event firstfocus
48194              * Fires when on first focus - needed by toolbars..
48195              * @param {HtmlEditor} this
48196              */
48197             firstfocus: true,
48198             /**
48199              * @event autosave
48200              * Auto save the htmlEditor value as a file into Events
48201              * @param {HtmlEditor} this
48202              */
48203             autosave: true,
48204             /**
48205              * @event savedpreview
48206              * preview the saved version of htmlEditor
48207              * @param {HtmlEditor} this
48208              */
48209             savedpreview: true,
48210             
48211             /**
48212             * @event stylesheetsclick
48213             * Fires when press the Sytlesheets button
48214             * @param {Roo.HtmlEditorCore} this
48215             */
48216             stylesheetsclick: true,
48217             /**
48218             * @event paste
48219             * Fires when press user pastes into the editor
48220             * @param {Roo.HtmlEditorCore} this
48221             */
48222             paste: true 
48223         });
48224         this.defaultAutoCreate =  {
48225             tag: "textarea",
48226             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48227             autocomplete: "new-password"
48228         };
48229     },
48230
48231     /**
48232      * Protected method that will not generally be called directly. It
48233      * is called when the editor creates its toolbar. Override this method if you need to
48234      * add custom toolbar buttons.
48235      * @param {HtmlEditor} editor
48236      */
48237     createToolbar : function(editor){
48238         Roo.log("create toolbars");
48239         if (!editor.toolbars || !editor.toolbars.length) {
48240             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48241         }
48242         
48243         for (var i =0 ; i < editor.toolbars.length;i++) {
48244             editor.toolbars[i] = Roo.factory(
48245                     typeof(editor.toolbars[i]) == 'string' ?
48246                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48247                 Roo.form.HtmlEditor);
48248             editor.toolbars[i].init(editor);
48249         }
48250          
48251         
48252     },
48253
48254      
48255     // private
48256     onRender : function(ct, position)
48257     {
48258         var _t = this;
48259         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48260         
48261         this.wrap = this.el.wrap({
48262             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48263         });
48264         
48265         this.editorcore.onRender(ct, position);
48266          
48267         if (this.resizable) {
48268             this.resizeEl = new Roo.Resizable(this.wrap, {
48269                 pinned : true,
48270                 wrap: true,
48271                 dynamic : true,
48272                 minHeight : this.height,
48273                 height: this.height,
48274                 handles : this.resizable,
48275                 width: this.width,
48276                 listeners : {
48277                     resize : function(r, w, h) {
48278                         _t.onResize(w,h); // -something
48279                     }
48280                 }
48281             });
48282             
48283         }
48284         this.createToolbar(this);
48285        
48286         
48287         if(!this.width){
48288             this.setSize(this.wrap.getSize());
48289         }
48290         if (this.resizeEl) {
48291             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48292             // should trigger onReize..
48293         }
48294         
48295         this.keyNav = new Roo.KeyNav(this.el, {
48296             
48297             "tab" : function(e){
48298                 e.preventDefault();
48299                 
48300                 var value = this.getValue();
48301                 
48302                 var start = this.el.dom.selectionStart;
48303                 var end = this.el.dom.selectionEnd;
48304                 
48305                 if(!e.shiftKey){
48306                     
48307                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48308                     this.el.dom.setSelectionRange(end + 1, end + 1);
48309                     return;
48310                 }
48311                 
48312                 var f = value.substring(0, start).split("\t");
48313                 
48314                 if(f.pop().length != 0){
48315                     return;
48316                 }
48317                 
48318                 this.setValue(f.join("\t") + value.substring(end));
48319                 this.el.dom.setSelectionRange(start - 1, start - 1);
48320                 
48321             },
48322             
48323             "home" : function(e){
48324                 e.preventDefault();
48325                 
48326                 var curr = this.el.dom.selectionStart;
48327                 var lines = this.getValue().split("\n");
48328                 
48329                 if(!lines.length){
48330                     return;
48331                 }
48332                 
48333                 if(e.ctrlKey){
48334                     this.el.dom.setSelectionRange(0, 0);
48335                     return;
48336                 }
48337                 
48338                 var pos = 0;
48339                 
48340                 for (var i = 0; i < lines.length;i++) {
48341                     pos += lines[i].length;
48342                     
48343                     if(i != 0){
48344                         pos += 1;
48345                     }
48346                     
48347                     if(pos < curr){
48348                         continue;
48349                     }
48350                     
48351                     pos -= lines[i].length;
48352                     
48353                     break;
48354                 }
48355                 
48356                 if(!e.shiftKey){
48357                     this.el.dom.setSelectionRange(pos, pos);
48358                     return;
48359                 }
48360                 
48361                 this.el.dom.selectionStart = pos;
48362                 this.el.dom.selectionEnd = curr;
48363             },
48364             
48365             "end" : function(e){
48366                 e.preventDefault();
48367                 
48368                 var curr = this.el.dom.selectionStart;
48369                 var lines = this.getValue().split("\n");
48370                 
48371                 if(!lines.length){
48372                     return;
48373                 }
48374                 
48375                 if(e.ctrlKey){
48376                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48377                     return;
48378                 }
48379                 
48380                 var pos = 0;
48381                 
48382                 for (var i = 0; i < lines.length;i++) {
48383                     
48384                     pos += lines[i].length;
48385                     
48386                     if(i != 0){
48387                         pos += 1;
48388                     }
48389                     
48390                     if(pos < curr){
48391                         continue;
48392                     }
48393                     
48394                     break;
48395                 }
48396                 
48397                 if(!e.shiftKey){
48398                     this.el.dom.setSelectionRange(pos, pos);
48399                     return;
48400                 }
48401                 
48402                 this.el.dom.selectionStart = curr;
48403                 this.el.dom.selectionEnd = pos;
48404             },
48405
48406             scope : this,
48407
48408             doRelay : function(foo, bar, hname){
48409                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48410             },
48411
48412             forceKeyDown: true
48413         });
48414         
48415 //        if(this.autosave && this.w){
48416 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48417 //        }
48418     },
48419
48420     // private
48421     onResize : function(w, h)
48422     {
48423         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48424         var ew = false;
48425         var eh = false;
48426         
48427         if(this.el ){
48428             if(typeof w == 'number'){
48429                 var aw = w - this.wrap.getFrameWidth('lr');
48430                 this.el.setWidth(this.adjustWidth('textarea', aw));
48431                 ew = aw;
48432             }
48433             if(typeof h == 'number'){
48434                 var tbh = 0;
48435                 for (var i =0; i < this.toolbars.length;i++) {
48436                     // fixme - ask toolbars for heights?
48437                     tbh += this.toolbars[i].tb.el.getHeight();
48438                     if (this.toolbars[i].footer) {
48439                         tbh += this.toolbars[i].footer.el.getHeight();
48440                     }
48441                 }
48442                 
48443                 
48444                 
48445                 
48446                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48447                 ah -= 5; // knock a few pixes off for look..
48448 //                Roo.log(ah);
48449                 this.el.setHeight(this.adjustWidth('textarea', ah));
48450                 var eh = ah;
48451             }
48452         }
48453         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48454         this.editorcore.onResize(ew,eh);
48455         
48456     },
48457
48458     /**
48459      * Toggles the editor between standard and source edit mode.
48460      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48461      */
48462     toggleSourceEdit : function(sourceEditMode)
48463     {
48464         this.editorcore.toggleSourceEdit(sourceEditMode);
48465         
48466         if(this.editorcore.sourceEditMode){
48467             Roo.log('editor - showing textarea');
48468             
48469 //            Roo.log('in');
48470 //            Roo.log(this.syncValue());
48471             this.editorcore.syncValue();
48472             this.el.removeClass('x-hidden');
48473             this.el.dom.removeAttribute('tabIndex');
48474             this.el.focus();
48475             this.el.dom.scrollTop = 0;
48476             
48477             
48478             for (var i = 0; i < this.toolbars.length; i++) {
48479                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48480                     this.toolbars[i].tb.hide();
48481                     this.toolbars[i].footer.hide();
48482                 }
48483             }
48484             
48485         }else{
48486             Roo.log('editor - hiding textarea');
48487 //            Roo.log('out')
48488 //            Roo.log(this.pushValue()); 
48489             this.editorcore.pushValue();
48490             
48491             this.el.addClass('x-hidden');
48492             this.el.dom.setAttribute('tabIndex', -1);
48493             
48494             for (var i = 0; i < this.toolbars.length; i++) {
48495                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48496                     this.toolbars[i].tb.show();
48497                     this.toolbars[i].footer.show();
48498                 }
48499             }
48500             
48501             //this.deferFocus();
48502         }
48503         
48504         this.setSize(this.wrap.getSize());
48505         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48506         
48507         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48508     },
48509  
48510     // private (for BoxComponent)
48511     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48512
48513     // private (for BoxComponent)
48514     getResizeEl : function(){
48515         return this.wrap;
48516     },
48517
48518     // private (for BoxComponent)
48519     getPositionEl : function(){
48520         return this.wrap;
48521     },
48522
48523     // private
48524     initEvents : function(){
48525         this.originalValue = this.getValue();
48526     },
48527
48528     /**
48529      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48530      * @method
48531      */
48532     markInvalid : Roo.emptyFn,
48533     /**
48534      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48535      * @method
48536      */
48537     clearInvalid : Roo.emptyFn,
48538
48539     setValue : function(v){
48540         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48541         this.editorcore.pushValue();
48542     },
48543
48544      
48545     // private
48546     deferFocus : function(){
48547         this.focus.defer(10, this);
48548     },
48549
48550     // doc'ed in Field
48551     focus : function(){
48552         this.editorcore.focus();
48553         
48554     },
48555       
48556
48557     // private
48558     onDestroy : function(){
48559         
48560         
48561         
48562         if(this.rendered){
48563             
48564             for (var i =0; i < this.toolbars.length;i++) {
48565                 // fixme - ask toolbars for heights?
48566                 this.toolbars[i].onDestroy();
48567             }
48568             
48569             this.wrap.dom.innerHTML = '';
48570             this.wrap.remove();
48571         }
48572     },
48573
48574     // private
48575     onFirstFocus : function(){
48576         //Roo.log("onFirstFocus");
48577         this.editorcore.onFirstFocus();
48578          for (var i =0; i < this.toolbars.length;i++) {
48579             this.toolbars[i].onFirstFocus();
48580         }
48581         
48582     },
48583     
48584     // private
48585     syncValue : function()
48586     {
48587         this.editorcore.syncValue();
48588     },
48589     
48590     pushValue : function()
48591     {
48592         this.editorcore.pushValue();
48593     },
48594     
48595     setStylesheets : function(stylesheets)
48596     {
48597         this.editorcore.setStylesheets(stylesheets);
48598     },
48599     
48600     removeStylesheets : function()
48601     {
48602         this.editorcore.removeStylesheets();
48603     }
48604      
48605     
48606     // hide stuff that is not compatible
48607     /**
48608      * @event blur
48609      * @hide
48610      */
48611     /**
48612      * @event change
48613      * @hide
48614      */
48615     /**
48616      * @event focus
48617      * @hide
48618      */
48619     /**
48620      * @event specialkey
48621      * @hide
48622      */
48623     /**
48624      * @cfg {String} fieldClass @hide
48625      */
48626     /**
48627      * @cfg {String} focusClass @hide
48628      */
48629     /**
48630      * @cfg {String} autoCreate @hide
48631      */
48632     /**
48633      * @cfg {String} inputType @hide
48634      */
48635     /**
48636      * @cfg {String} invalidClass @hide
48637      */
48638     /**
48639      * @cfg {String} invalidText @hide
48640      */
48641     /**
48642      * @cfg {String} msgFx @hide
48643      */
48644     /**
48645      * @cfg {String} validateOnBlur @hide
48646      */
48647 });
48648  
48649     // <script type="text/javascript">
48650 /*
48651  * Based on
48652  * Ext JS Library 1.1.1
48653  * Copyright(c) 2006-2007, Ext JS, LLC.
48654  *  
48655  
48656  */
48657
48658 /**
48659  * @class Roo.form.HtmlEditorToolbar1
48660  * Basic Toolbar
48661  * 
48662  * Usage:
48663  *
48664  new Roo.form.HtmlEditor({
48665     ....
48666     toolbars : [
48667         new Roo.form.HtmlEditorToolbar1({
48668             disable : { fonts: 1 , format: 1, ..., ... , ...],
48669             btns : [ .... ]
48670         })
48671     }
48672      
48673  * 
48674  * @cfg {Object} disable List of elements to disable..
48675  * @cfg {Array} btns List of additional buttons.
48676  * 
48677  * 
48678  * NEEDS Extra CSS? 
48679  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48680  */
48681  
48682 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48683 {
48684     
48685     Roo.apply(this, config);
48686     
48687     // default disabled, based on 'good practice'..
48688     this.disable = this.disable || {};
48689     Roo.applyIf(this.disable, {
48690         fontSize : true,
48691         colors : true,
48692         specialElements : true
48693     });
48694     
48695     
48696     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48697     // dont call parent... till later.
48698 }
48699
48700 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48701     
48702     tb: false,
48703     
48704     rendered: false,
48705     
48706     editor : false,
48707     editorcore : false,
48708     /**
48709      * @cfg {Object} disable  List of toolbar elements to disable
48710          
48711      */
48712     disable : false,
48713     
48714     
48715      /**
48716      * @cfg {String} createLinkText The default text for the create link prompt
48717      */
48718     createLinkText : 'Please enter the URL for the link:',
48719     /**
48720      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48721      */
48722     defaultLinkValue : 'http:/'+'/',
48723    
48724     
48725       /**
48726      * @cfg {Array} fontFamilies An array of available font families
48727      */
48728     fontFamilies : [
48729         'Arial',
48730         'Courier New',
48731         'Tahoma',
48732         'Times New Roman',
48733         'Verdana'
48734     ],
48735     
48736     specialChars : [
48737            "&#169;",
48738           "&#174;",     
48739           "&#8482;",    
48740           "&#163;" ,    
48741          // "&#8212;",    
48742           "&#8230;",    
48743           "&#247;" ,    
48744         //  "&#225;" ,     ?? a acute?
48745            "&#8364;"    , //Euro
48746        //   "&#8220;"    ,
48747         //  "&#8221;"    ,
48748         //  "&#8226;"    ,
48749           "&#176;"  //   , // degrees
48750
48751          // "&#233;"     , // e ecute
48752          // "&#250;"     , // u ecute?
48753     ],
48754     
48755     specialElements : [
48756         {
48757             text: "Insert Table",
48758             xtype: 'MenuItem',
48759             xns : Roo.Menu,
48760             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48761                 
48762         },
48763         {    
48764             text: "Insert Image",
48765             xtype: 'MenuItem',
48766             xns : Roo.Menu,
48767             ihtml : '<img src="about:blank"/>'
48768             
48769         }
48770         
48771          
48772     ],
48773     
48774     
48775     inputElements : [ 
48776             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48777             "input:submit", "input:button", "select", "textarea", "label" ],
48778     formats : [
48779         ["p"] ,  
48780         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48781         ["pre"],[ "code"], 
48782         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48783         ['div'],['span'],
48784         ['sup'],['sub']
48785     ],
48786     
48787     cleanStyles : [
48788         "font-size"
48789     ],
48790      /**
48791      * @cfg {String} defaultFont default font to use.
48792      */
48793     defaultFont: 'tahoma',
48794    
48795     fontSelect : false,
48796     
48797     
48798     formatCombo : false,
48799     
48800     init : function(editor)
48801     {
48802         this.editor = editor;
48803         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48804         var editorcore = this.editorcore;
48805         
48806         var _t = this;
48807         
48808         var fid = editorcore.frameId;
48809         var etb = this;
48810         function btn(id, toggle, handler){
48811             var xid = fid + '-'+ id ;
48812             return {
48813                 id : xid,
48814                 cmd : id,
48815                 cls : 'x-btn-icon x-edit-'+id,
48816                 enableToggle:toggle !== false,
48817                 scope: _t, // was editor...
48818                 handler:handler||_t.relayBtnCmd,
48819                 clickEvent:'mousedown',
48820                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48821                 tabIndex:-1
48822             };
48823         }
48824         
48825         
48826         
48827         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48828         this.tb = tb;
48829          // stop form submits
48830         tb.el.on('click', function(e){
48831             e.preventDefault(); // what does this do?
48832         });
48833
48834         if(!this.disable.font) { // && !Roo.isSafari){
48835             /* why no safari for fonts 
48836             editor.fontSelect = tb.el.createChild({
48837                 tag:'select',
48838                 tabIndex: -1,
48839                 cls:'x-font-select',
48840                 html: this.createFontOptions()
48841             });
48842             
48843             editor.fontSelect.on('change', function(){
48844                 var font = editor.fontSelect.dom.value;
48845                 editor.relayCmd('fontname', font);
48846                 editor.deferFocus();
48847             }, editor);
48848             
48849             tb.add(
48850                 editor.fontSelect.dom,
48851                 '-'
48852             );
48853             */
48854             
48855         };
48856         if(!this.disable.formats){
48857             this.formatCombo = new Roo.form.ComboBox({
48858                 store: new Roo.data.SimpleStore({
48859                     id : 'tag',
48860                     fields: ['tag'],
48861                     data : this.formats // from states.js
48862                 }),
48863                 blockFocus : true,
48864                 name : '',
48865                 //autoCreate : {tag: "div",  size: "20"},
48866                 displayField:'tag',
48867                 typeAhead: false,
48868                 mode: 'local',
48869                 editable : false,
48870                 triggerAction: 'all',
48871                 emptyText:'Add tag',
48872                 selectOnFocus:true,
48873                 width:135,
48874                 listeners : {
48875                     'select': function(c, r, i) {
48876                         editorcore.insertTag(r.get('tag'));
48877                         editor.focus();
48878                     }
48879                 }
48880
48881             });
48882             tb.addField(this.formatCombo);
48883             
48884         }
48885         
48886         if(!this.disable.format){
48887             tb.add(
48888                 btn('bold'),
48889                 btn('italic'),
48890                 btn('underline'),
48891                 btn('strikethrough')
48892             );
48893         };
48894         if(!this.disable.fontSize){
48895             tb.add(
48896                 '-',
48897                 
48898                 
48899                 btn('increasefontsize', false, editorcore.adjustFont),
48900                 btn('decreasefontsize', false, editorcore.adjustFont)
48901             );
48902         };
48903         
48904         
48905         if(!this.disable.colors){
48906             tb.add(
48907                 '-', {
48908                     id:editorcore.frameId +'-forecolor',
48909                     cls:'x-btn-icon x-edit-forecolor',
48910                     clickEvent:'mousedown',
48911                     tooltip: this.buttonTips['forecolor'] || undefined,
48912                     tabIndex:-1,
48913                     menu : new Roo.menu.ColorMenu({
48914                         allowReselect: true,
48915                         focus: Roo.emptyFn,
48916                         value:'000000',
48917                         plain:true,
48918                         selectHandler: function(cp, color){
48919                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48920                             editor.deferFocus();
48921                         },
48922                         scope: editorcore,
48923                         clickEvent:'mousedown'
48924                     })
48925                 }, {
48926                     id:editorcore.frameId +'backcolor',
48927                     cls:'x-btn-icon x-edit-backcolor',
48928                     clickEvent:'mousedown',
48929                     tooltip: this.buttonTips['backcolor'] || undefined,
48930                     tabIndex:-1,
48931                     menu : new Roo.menu.ColorMenu({
48932                         focus: Roo.emptyFn,
48933                         value:'FFFFFF',
48934                         plain:true,
48935                         allowReselect: true,
48936                         selectHandler: function(cp, color){
48937                             if(Roo.isGecko){
48938                                 editorcore.execCmd('useCSS', false);
48939                                 editorcore.execCmd('hilitecolor', color);
48940                                 editorcore.execCmd('useCSS', true);
48941                                 editor.deferFocus();
48942                             }else{
48943                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48944                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48945                                 editor.deferFocus();
48946                             }
48947                         },
48948                         scope:editorcore,
48949                         clickEvent:'mousedown'
48950                     })
48951                 }
48952             );
48953         };
48954         // now add all the items...
48955         
48956
48957         if(!this.disable.alignments){
48958             tb.add(
48959                 '-',
48960                 btn('justifyleft'),
48961                 btn('justifycenter'),
48962                 btn('justifyright')
48963             );
48964         };
48965
48966         //if(!Roo.isSafari){
48967             if(!this.disable.links){
48968                 tb.add(
48969                     '-',
48970                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48971                 );
48972             };
48973
48974             if(!this.disable.lists){
48975                 tb.add(
48976                     '-',
48977                     btn('insertorderedlist'),
48978                     btn('insertunorderedlist')
48979                 );
48980             }
48981             if(!this.disable.sourceEdit){
48982                 tb.add(
48983                     '-',
48984                     btn('sourceedit', true, function(btn){
48985                         this.toggleSourceEdit(btn.pressed);
48986                     })
48987                 );
48988             }
48989         //}
48990         
48991         var smenu = { };
48992         // special menu.. - needs to be tidied up..
48993         if (!this.disable.special) {
48994             smenu = {
48995                 text: "&#169;",
48996                 cls: 'x-edit-none',
48997                 
48998                 menu : {
48999                     items : []
49000                 }
49001             };
49002             for (var i =0; i < this.specialChars.length; i++) {
49003                 smenu.menu.items.push({
49004                     
49005                     html: this.specialChars[i],
49006                     handler: function(a,b) {
49007                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49008                         //editor.insertAtCursor(a.html);
49009                         
49010                     },
49011                     tabIndex:-1
49012                 });
49013             }
49014             
49015             
49016             tb.add(smenu);
49017             
49018             
49019         }
49020         
49021         var cmenu = { };
49022         if (!this.disable.cleanStyles) {
49023             cmenu = {
49024                 cls: 'x-btn-icon x-btn-clear',
49025                 
49026                 menu : {
49027                     items : []
49028                 }
49029             };
49030             for (var i =0; i < this.cleanStyles.length; i++) {
49031                 cmenu.menu.items.push({
49032                     actiontype : this.cleanStyles[i],
49033                     html: 'Remove ' + this.cleanStyles[i],
49034                     handler: function(a,b) {
49035 //                        Roo.log(a);
49036 //                        Roo.log(b);
49037                         var c = Roo.get(editorcore.doc.body);
49038                         c.select('[style]').each(function(s) {
49039                             s.dom.style.removeProperty(a.actiontype);
49040                         });
49041                         editorcore.syncValue();
49042                     },
49043                     tabIndex:-1
49044                 });
49045             }
49046             cmenu.menu.items.push({
49047                 actiontype : 'tablewidths',
49048                 html: 'Remove Table Widths',
49049                 handler: function(a,b) {
49050                     editorcore.cleanTableWidths();
49051                     editorcore.syncValue();
49052                 },
49053                 tabIndex:-1
49054             });
49055             cmenu.menu.items.push({
49056                 actiontype : 'word',
49057                 html: 'Remove MS Word Formating',
49058                 handler: function(a,b) {
49059                     editorcore.cleanWord();
49060                     editorcore.syncValue();
49061                 },
49062                 tabIndex:-1
49063             });
49064             
49065             cmenu.menu.items.push({
49066                 actiontype : 'all',
49067                 html: 'Remove All Styles',
49068                 handler: function(a,b) {
49069                     
49070                     var c = Roo.get(editorcore.doc.body);
49071                     c.select('[style]').each(function(s) {
49072                         s.dom.removeAttribute('style');
49073                     });
49074                     editorcore.syncValue();
49075                 },
49076                 tabIndex:-1
49077             });
49078             
49079             cmenu.menu.items.push({
49080                 actiontype : 'all',
49081                 html: 'Remove All CSS Classes',
49082                 handler: function(a,b) {
49083                     
49084                     var c = Roo.get(editorcore.doc.body);
49085                     c.select('[class]').each(function(s) {
49086                         s.dom.removeAttribute('class');
49087                     });
49088                     editorcore.cleanWord();
49089                     editorcore.syncValue();
49090                 },
49091                 tabIndex:-1
49092             });
49093             
49094              cmenu.menu.items.push({
49095                 actiontype : 'tidy',
49096                 html: 'Tidy HTML Source',
49097                 handler: function(a,b) {
49098                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49099                     editorcore.syncValue();
49100                 },
49101                 tabIndex:-1
49102             });
49103             
49104             
49105             tb.add(cmenu);
49106         }
49107          
49108         if (!this.disable.specialElements) {
49109             var semenu = {
49110                 text: "Other;",
49111                 cls: 'x-edit-none',
49112                 menu : {
49113                     items : []
49114                 }
49115             };
49116             for (var i =0; i < this.specialElements.length; i++) {
49117                 semenu.menu.items.push(
49118                     Roo.apply({ 
49119                         handler: function(a,b) {
49120                             editor.insertAtCursor(this.ihtml);
49121                         }
49122                     }, this.specialElements[i])
49123                 );
49124                     
49125             }
49126             
49127             tb.add(semenu);
49128             
49129             
49130         }
49131          
49132         
49133         if (this.btns) {
49134             for(var i =0; i< this.btns.length;i++) {
49135                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49136                 b.cls =  'x-edit-none';
49137                 
49138                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49139                     b.cls += ' x-init-enable';
49140                 }
49141                 
49142                 b.scope = editorcore;
49143                 tb.add(b);
49144             }
49145         
49146         }
49147         
49148         
49149         
49150         // disable everything...
49151         
49152         this.tb.items.each(function(item){
49153             
49154            if(
49155                 item.id != editorcore.frameId+ '-sourceedit' && 
49156                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49157             ){
49158                 
49159                 item.disable();
49160             }
49161         });
49162         this.rendered = true;
49163         
49164         // the all the btns;
49165         editor.on('editorevent', this.updateToolbar, this);
49166         // other toolbars need to implement this..
49167         //editor.on('editmodechange', this.updateToolbar, this);
49168     },
49169     
49170     
49171     relayBtnCmd : function(btn) {
49172         this.editorcore.relayCmd(btn.cmd);
49173     },
49174     // private used internally
49175     createLink : function(){
49176         Roo.log("create link?");
49177         var url = prompt(this.createLinkText, this.defaultLinkValue);
49178         if(url && url != 'http:/'+'/'){
49179             this.editorcore.relayCmd('createlink', url);
49180         }
49181     },
49182
49183     
49184     /**
49185      * Protected method that will not generally be called directly. It triggers
49186      * a toolbar update by reading the markup state of the current selection in the editor.
49187      */
49188     updateToolbar: function(){
49189
49190         if(!this.editorcore.activated){
49191             this.editor.onFirstFocus();
49192             return;
49193         }
49194
49195         var btns = this.tb.items.map, 
49196             doc = this.editorcore.doc,
49197             frameId = this.editorcore.frameId;
49198
49199         if(!this.disable.font && !Roo.isSafari){
49200             /*
49201             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49202             if(name != this.fontSelect.dom.value){
49203                 this.fontSelect.dom.value = name;
49204             }
49205             */
49206         }
49207         if(!this.disable.format){
49208             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49209             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49210             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49211             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49212         }
49213         if(!this.disable.alignments){
49214             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49215             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49216             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49217         }
49218         if(!Roo.isSafari && !this.disable.lists){
49219             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49220             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49221         }
49222         
49223         var ans = this.editorcore.getAllAncestors();
49224         if (this.formatCombo) {
49225             
49226             
49227             var store = this.formatCombo.store;
49228             this.formatCombo.setValue("");
49229             for (var i =0; i < ans.length;i++) {
49230                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49231                     // select it..
49232                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49233                     break;
49234                 }
49235             }
49236         }
49237         
49238         
49239         
49240         // hides menus... - so this cant be on a menu...
49241         Roo.menu.MenuMgr.hideAll();
49242
49243         //this.editorsyncValue();
49244     },
49245    
49246     
49247     createFontOptions : function(){
49248         var buf = [], fs = this.fontFamilies, ff, lc;
49249         
49250         
49251         
49252         for(var i = 0, len = fs.length; i< len; i++){
49253             ff = fs[i];
49254             lc = ff.toLowerCase();
49255             buf.push(
49256                 '<option value="',lc,'" style="font-family:',ff,';"',
49257                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49258                     ff,
49259                 '</option>'
49260             );
49261         }
49262         return buf.join('');
49263     },
49264     
49265     toggleSourceEdit : function(sourceEditMode){
49266         
49267         Roo.log("toolbar toogle");
49268         if(sourceEditMode === undefined){
49269             sourceEditMode = !this.sourceEditMode;
49270         }
49271         this.sourceEditMode = sourceEditMode === true;
49272         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49273         // just toggle the button?
49274         if(btn.pressed !== this.sourceEditMode){
49275             btn.toggle(this.sourceEditMode);
49276             return;
49277         }
49278         
49279         if(sourceEditMode){
49280             Roo.log("disabling buttons");
49281             this.tb.items.each(function(item){
49282                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49283                     item.disable();
49284                 }
49285             });
49286           
49287         }else{
49288             Roo.log("enabling buttons");
49289             if(this.editorcore.initialized){
49290                 this.tb.items.each(function(item){
49291                     item.enable();
49292                 });
49293             }
49294             
49295         }
49296         Roo.log("calling toggole on editor");
49297         // tell the editor that it's been pressed..
49298         this.editor.toggleSourceEdit(sourceEditMode);
49299        
49300     },
49301      /**
49302      * Object collection of toolbar tooltips for the buttons in the editor. The key
49303      * is the command id associated with that button and the value is a valid QuickTips object.
49304      * For example:
49305 <pre><code>
49306 {
49307     bold : {
49308         title: 'Bold (Ctrl+B)',
49309         text: 'Make the selected text bold.',
49310         cls: 'x-html-editor-tip'
49311     },
49312     italic : {
49313         title: 'Italic (Ctrl+I)',
49314         text: 'Make the selected text italic.',
49315         cls: 'x-html-editor-tip'
49316     },
49317     ...
49318 </code></pre>
49319     * @type Object
49320      */
49321     buttonTips : {
49322         bold : {
49323             title: 'Bold (Ctrl+B)',
49324             text: 'Make the selected text bold.',
49325             cls: 'x-html-editor-tip'
49326         },
49327         italic : {
49328             title: 'Italic (Ctrl+I)',
49329             text: 'Make the selected text italic.',
49330             cls: 'x-html-editor-tip'
49331         },
49332         underline : {
49333             title: 'Underline (Ctrl+U)',
49334             text: 'Underline the selected text.',
49335             cls: 'x-html-editor-tip'
49336         },
49337         strikethrough : {
49338             title: 'Strikethrough',
49339             text: 'Strikethrough the selected text.',
49340             cls: 'x-html-editor-tip'
49341         },
49342         increasefontsize : {
49343             title: 'Grow Text',
49344             text: 'Increase the font size.',
49345             cls: 'x-html-editor-tip'
49346         },
49347         decreasefontsize : {
49348             title: 'Shrink Text',
49349             text: 'Decrease the font size.',
49350             cls: 'x-html-editor-tip'
49351         },
49352         backcolor : {
49353             title: 'Text Highlight Color',
49354             text: 'Change the background color of the selected text.',
49355             cls: 'x-html-editor-tip'
49356         },
49357         forecolor : {
49358             title: 'Font Color',
49359             text: 'Change the color of the selected text.',
49360             cls: 'x-html-editor-tip'
49361         },
49362         justifyleft : {
49363             title: 'Align Text Left',
49364             text: 'Align text to the left.',
49365             cls: 'x-html-editor-tip'
49366         },
49367         justifycenter : {
49368             title: 'Center Text',
49369             text: 'Center text in the editor.',
49370             cls: 'x-html-editor-tip'
49371         },
49372         justifyright : {
49373             title: 'Align Text Right',
49374             text: 'Align text to the right.',
49375             cls: 'x-html-editor-tip'
49376         },
49377         insertunorderedlist : {
49378             title: 'Bullet List',
49379             text: 'Start a bulleted list.',
49380             cls: 'x-html-editor-tip'
49381         },
49382         insertorderedlist : {
49383             title: 'Numbered List',
49384             text: 'Start a numbered list.',
49385             cls: 'x-html-editor-tip'
49386         },
49387         createlink : {
49388             title: 'Hyperlink',
49389             text: 'Make the selected text a hyperlink.',
49390             cls: 'x-html-editor-tip'
49391         },
49392         sourceedit : {
49393             title: 'Source Edit',
49394             text: 'Switch to source editing mode.',
49395             cls: 'x-html-editor-tip'
49396         }
49397     },
49398     // private
49399     onDestroy : function(){
49400         if(this.rendered){
49401             
49402             this.tb.items.each(function(item){
49403                 if(item.menu){
49404                     item.menu.removeAll();
49405                     if(item.menu.el){
49406                         item.menu.el.destroy();
49407                     }
49408                 }
49409                 item.destroy();
49410             });
49411              
49412         }
49413     },
49414     onFirstFocus: function() {
49415         this.tb.items.each(function(item){
49416            item.enable();
49417         });
49418     }
49419 });
49420
49421
49422
49423
49424 // <script type="text/javascript">
49425 /*
49426  * Based on
49427  * Ext JS Library 1.1.1
49428  * Copyright(c) 2006-2007, Ext JS, LLC.
49429  *  
49430  
49431  */
49432
49433  
49434 /**
49435  * @class Roo.form.HtmlEditor.ToolbarContext
49436  * Context Toolbar
49437  * 
49438  * Usage:
49439  *
49440  new Roo.form.HtmlEditor({
49441     ....
49442     toolbars : [
49443         { xtype: 'ToolbarStandard', styles : {} }
49444         { xtype: 'ToolbarContext', disable : {} }
49445     ]
49446 })
49447
49448      
49449  * 
49450  * @config : {Object} disable List of elements to disable.. (not done yet.)
49451  * @config : {Object} styles  Map of styles available.
49452  * 
49453  */
49454
49455 Roo.form.HtmlEditor.ToolbarContext = function(config)
49456 {
49457     
49458     Roo.apply(this, config);
49459     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49460     // dont call parent... till later.
49461     this.styles = this.styles || {};
49462 }
49463
49464  
49465
49466 Roo.form.HtmlEditor.ToolbarContext.types = {
49467     'IMG' : [
49468         {
49469             name : 'width',
49470             title: "Width",
49471             width: 40
49472         },
49473         {
49474             name : 'height',
49475             title: "Height",
49476             width: 40
49477         },
49478         {
49479             name : 'align',
49480             title: "Align",
49481             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49482             width : 80
49483             
49484         },
49485         {
49486             name : 'border',
49487             title: "Border",
49488             width: 40
49489         },
49490         {
49491             name : 'alt',
49492             title: "Alt",
49493             width: 120
49494         },
49495         {
49496             name : 'src',
49497             title: "Src",
49498             width: 220
49499         }
49500         
49501     ],
49502     
49503     'FIGURE' : [
49504         {
49505             name : 'align',
49506             title: "Align",
49507             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49508             width : 80  
49509         }
49510     ],
49511     'A' : [
49512         {
49513             name : 'name',
49514             title: "Name",
49515             width: 50
49516         },
49517         {
49518             name : 'target',
49519             title: "Target",
49520             width: 120
49521         },
49522         {
49523             name : 'href',
49524             title: "Href",
49525             width: 220
49526         } // border?
49527         
49528     ],
49529     
49530     'INPUT' : [
49531         {
49532             name : 'name',
49533             title: "name",
49534             width: 120
49535         },
49536         {
49537             name : 'value',
49538             title: "Value",
49539             width: 120
49540         },
49541         {
49542             name : 'width',
49543             title: "Width",
49544             width: 40
49545         }
49546     ],
49547     'LABEL' : [
49548          {
49549             name : 'for',
49550             title: "For",
49551             width: 120
49552         }
49553     ],
49554     'TEXTAREA' : [
49555         {
49556             name : 'name',
49557             title: "name",
49558             width: 120
49559         },
49560         {
49561             name : 'rows',
49562             title: "Rows",
49563             width: 20
49564         },
49565         {
49566             name : 'cols',
49567             title: "Cols",
49568             width: 20
49569         }
49570     ],
49571     'SELECT' : [
49572         {
49573             name : 'name',
49574             title: "name",
49575             width: 120
49576         },
49577         {
49578             name : 'selectoptions',
49579             title: "Options",
49580             width: 200
49581         }
49582     ],
49583     
49584     // should we really allow this??
49585     // should this just be 
49586     'BODY' : [
49587         
49588         {
49589             name : 'title',
49590             title: "Title",
49591             width: 200,
49592             disabled : true
49593         }
49594     ],
49595  
49596     '*' : [
49597         // empty.
49598     ]
49599
49600 };
49601
49602 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49603 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49604
49605 Roo.form.HtmlEditor.ToolbarContext.options = {
49606         'font-family'  : [ 
49607                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49608                 [ 'Courier New', 'Courier New'],
49609                 [ 'Tahoma', 'Tahoma'],
49610                 [ 'Times New Roman,serif', 'Times'],
49611                 [ 'Verdana','Verdana' ]
49612         ]
49613 };
49614
49615 // fixme - these need to be configurable..
49616  
49617
49618 //Roo.form.HtmlEditor.ToolbarContext.types
49619
49620
49621 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49622     
49623     tb: false,
49624     
49625     rendered: false,
49626     
49627     editor : false,
49628     editorcore : false,
49629     /**
49630      * @cfg {Object} disable  List of toolbar elements to disable
49631          
49632      */
49633     disable : false,
49634     /**
49635      * @cfg {Object} styles List of styles 
49636      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49637      *
49638      * These must be defined in the page, so they get rendered correctly..
49639      * .headline { }
49640      * TD.underline { }
49641      * 
49642      */
49643     styles : false,
49644     
49645     options: false,
49646     
49647     toolbars : false,
49648     
49649     init : function(editor)
49650     {
49651         this.editor = editor;
49652         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49653         var editorcore = this.editorcore;
49654         
49655         var fid = editorcore.frameId;
49656         var etb = this;
49657         function btn(id, toggle, handler){
49658             var xid = fid + '-'+ id ;
49659             return {
49660                 id : xid,
49661                 cmd : id,
49662                 cls : 'x-btn-icon x-edit-'+id,
49663                 enableToggle:toggle !== false,
49664                 scope: editorcore, // was editor...
49665                 handler:handler||editorcore.relayBtnCmd,
49666                 clickEvent:'mousedown',
49667                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49668                 tabIndex:-1
49669             };
49670         }
49671         // create a new element.
49672         var wdiv = editor.wrap.createChild({
49673                 tag: 'div'
49674             }, editor.wrap.dom.firstChild.nextSibling, true);
49675         
49676         // can we do this more than once??
49677         
49678          // stop form submits
49679       
49680  
49681         // disable everything...
49682         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49683         this.toolbars = {};
49684            
49685         for (var i in  ty) {
49686           
49687             this.toolbars[i] = this.buildToolbar(ty[i],i);
49688         }
49689         this.tb = this.toolbars.BODY;
49690         this.tb.el.show();
49691         this.buildFooter();
49692         this.footer.show();
49693         editor.on('hide', function( ) { this.footer.hide() }, this);
49694         editor.on('show', function( ) { this.footer.show() }, this);
49695         
49696          
49697         this.rendered = true;
49698         
49699         // the all the btns;
49700         editor.on('editorevent', this.updateToolbar, this);
49701         // other toolbars need to implement this..
49702         //editor.on('editmodechange', this.updateToolbar, this);
49703     },
49704     
49705     
49706     
49707     /**
49708      * Protected method that will not generally be called directly. It triggers
49709      * a toolbar update by reading the markup state of the current selection in the editor.
49710      *
49711      * Note you can force an update by calling on('editorevent', scope, false)
49712      */
49713     updateToolbar: function(editor ,ev, sel)
49714     {
49715         
49716         if (ev) {
49717             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49718         }
49719         
49720         //Roo.log(ev);
49721         // capture mouse up - this is handy for selecting images..
49722         // perhaps should go somewhere else...
49723         if(!this.editorcore.activated){
49724              this.editor.onFirstFocus();
49725             return;
49726         }
49727         //Roo.log(ev ? ev.target : 'NOTARGET');
49728         
49729         
49730         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49731         // selectNode - might want to handle IE?
49732         
49733         
49734         
49735         if (ev &&
49736             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49737             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49738             // they have click on an image...
49739             // let's see if we can change the selection...
49740             sel = ev.target;
49741             
49742             // this triggers looping?
49743             //this.editorcore.selectNode(sel);
49744              
49745         }  
49746         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49747         //Roo.get(node).addClass('roo-ed-selection');
49748       
49749         //var updateFooter = sel ? false : true; 
49750         
49751         
49752         var ans = this.editorcore.getAllAncestors();
49753         
49754         // pick
49755         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49756         
49757         if (!sel) { 
49758             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49759             sel = sel ? sel : this.editorcore.doc.body;
49760             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49761             
49762         }
49763         
49764         var tn = sel.tagName.toUpperCase();
49765         var lastSel = this.tb.selectedNode;
49766         this.tb.selectedNode = sel;
49767         var left_label = tn;
49768         
49769         // ok see if we are editing a block?
49770         
49771         var db = false;
49772         // you are not actually selecting the block.
49773         if (sel && sel.hasAttribute('data-block')) {
49774             db = sel;
49775         } else if (sel && !sel.hasAttribute('contenteditable')) {
49776             var sel_el = Roo.get(sel);
49777             db = sel_el.findParent('[data-block]');
49778             var cepar = sel_el.findParent('[contenteditable=true]');
49779             if (db && cepar && cepar.tagName != 'BODY') {
49780                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49781             }   
49782         }
49783         
49784         
49785         var block = false;
49786         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49787         if (db) {
49788             block = Roo.htmleditor.Block.factory(db);
49789             
49790             
49791             if (block) {
49792                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
49793                 tn = 'BLOCK.' + db.getAttribute('data-block');
49794                 
49795                 //this.editorcore.selectNode(db);
49796                 if (typeof(this.toolbars[tn]) == 'undefined') {
49797                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49798                 }
49799                 this.toolbars[tn].selectedNode = db;
49800                 left_label = block.friendly_name;
49801                 ans = this.editorcore.getAllAncestors();
49802             }
49803             
49804                 
49805             
49806         }
49807         
49808         
49809         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49810             return; // no change?
49811         }
49812         
49813         
49814           
49815         this.tb.el.hide();
49816         ///console.log("show: " + tn);
49817         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49818         
49819         this.tb.el.show();
49820         // update name
49821         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49822         
49823         
49824         // update attributes
49825         if (block && this.tb.fields) {
49826              
49827             this.tb.fields.each(function(e) {
49828                 e.setValue(block[e.name]);
49829             });
49830             
49831             
49832         } else  if (this.tb.fields && this.tb.selectedNode) {
49833             this.tb.fields.each( function(e) {
49834                 if (e.stylename) {
49835                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49836                     return;
49837                 } 
49838                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49839             }, this);
49840             this.updateToolbarStyles(this.tb.selectedNode);  
49841         }
49842         
49843         
49844        
49845         Roo.menu.MenuMgr.hideAll();
49846
49847         
49848         
49849     
49850         // update the footer
49851         //
49852         this.updateFooter(ans);
49853              
49854     },
49855     
49856     updateToolbarStyles : function(sel)
49857     {
49858         var hasStyles = false;
49859         for(var i in this.styles) {
49860             hasStyles = true;
49861             break;
49862         }
49863         
49864         // update styles
49865         if (hasStyles && this.tb.hasStyles) { 
49866             var st = this.tb.fields.item(0);
49867             
49868             st.store.removeAll();
49869             var cn = sel.className.split(/\s+/);
49870             
49871             var avs = [];
49872             if (this.styles['*']) {
49873                 
49874                 Roo.each(this.styles['*'], function(v) {
49875                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49876                 });
49877             }
49878             if (this.styles[tn]) { 
49879                 Roo.each(this.styles[tn], function(v) {
49880                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49881                 });
49882             }
49883             
49884             st.store.loadData(avs);
49885             st.collapse();
49886             st.setValue(cn);
49887         }
49888     },
49889     
49890      
49891     updateFooter : function(ans)
49892     {
49893         var html = '';
49894         if (ans === false) {
49895             this.footDisp.dom.innerHTML = '';
49896             return;
49897         }
49898         
49899         this.footerEls = ans.reverse();
49900         Roo.each(this.footerEls, function(a,i) {
49901             if (!a) { return; }
49902             html += html.length ? ' &gt; '  :  '';
49903             
49904             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49905             
49906         });
49907        
49908         // 
49909         var sz = this.footDisp.up('td').getSize();
49910         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49911         this.footDisp.dom.style.marginLeft = '5px';
49912         
49913         this.footDisp.dom.style.overflow = 'hidden';
49914         
49915         this.footDisp.dom.innerHTML = html;
49916             
49917         
49918     },
49919    
49920        
49921     // private
49922     onDestroy : function(){
49923         if(this.rendered){
49924             
49925             this.tb.items.each(function(item){
49926                 if(item.menu){
49927                     item.menu.removeAll();
49928                     if(item.menu.el){
49929                         item.menu.el.destroy();
49930                     }
49931                 }
49932                 item.destroy();
49933             });
49934              
49935         }
49936     },
49937     onFirstFocus: function() {
49938         // need to do this for all the toolbars..
49939         this.tb.items.each(function(item){
49940            item.enable();
49941         });
49942     },
49943     buildToolbar: function(tlist, nm, friendly_name, block)
49944     {
49945         var editor = this.editor;
49946         var editorcore = this.editorcore;
49947          // create a new element.
49948         var wdiv = editor.wrap.createChild({
49949                 tag: 'div'
49950             }, editor.wrap.dom.firstChild.nextSibling, true);
49951         
49952        
49953         var tb = new Roo.Toolbar(wdiv);
49954         ///this.tb = tb; // << this sets the active toolbar..
49955         if (tlist === false && block) {
49956             tlist = block.contextMenu(this);
49957         }
49958         
49959         tb.hasStyles = false;
49960         tb.name = nm;
49961         
49962         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49963         
49964         var styles = Array.from(this.styles);
49965         
49966         
49967         // styles...
49968         if (styles && styles.length) {
49969             tb.hasStyles = true;
49970             // this needs a multi-select checkbox...
49971             tb.addField( new Roo.form.ComboBox({
49972                 store: new Roo.data.SimpleStore({
49973                     id : 'val',
49974                     fields: ['val', 'selected'],
49975                     data : [] 
49976                 }),
49977                 name : '-roo-edit-className',
49978                 attrname : 'className',
49979                 displayField: 'val',
49980                 typeAhead: false,
49981                 mode: 'local',
49982                 editable : false,
49983                 triggerAction: 'all',
49984                 emptyText:'Select Style',
49985                 selectOnFocus:true,
49986                 width: 130,
49987                 listeners : {
49988                     'select': function(c, r, i) {
49989                         // initial support only for on class per el..
49990                         tb.selectedNode.className =  r ? r.get('val') : '';
49991                         editorcore.syncValue();
49992                     }
49993                 }
49994     
49995             }));
49996         }
49997         
49998         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49999         
50000         
50001         for (var i = 0; i < tlist.length; i++) {
50002             
50003             // newer versions will use xtype cfg to create menus.
50004             if (typeof(tlist[i].xtype) != 'undefined') {
50005                 
50006                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
50007                 
50008                 
50009                 continue;
50010             }
50011             
50012             var item = tlist[i];
50013             tb.add(item.title + ":&nbsp;");
50014             
50015             
50016             //optname == used so you can configure the options available..
50017             var opts = item.opts ? item.opts : false;
50018             if (item.optname) { // use the b
50019                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50020            
50021             }
50022             
50023             if (opts) {
50024                 // opts == pulldown..
50025                 tb.addField( new Roo.form.ComboBox({
50026                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50027                         id : 'val',
50028                         fields: ['val', 'display'],
50029                         data : opts  
50030                     }),
50031                     name : '-roo-edit-' + tlist[i].name,
50032                     
50033                     attrname : tlist[i].name,
50034                     stylename : item.style ? item.style : false,
50035                     
50036                     displayField: item.displayField ? item.displayField : 'val',
50037                     valueField :  'val',
50038                     typeAhead: false,
50039                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50040                     editable : false,
50041                     triggerAction: 'all',
50042                     emptyText:'Select',
50043                     selectOnFocus:true,
50044                     width: item.width ? item.width  : 130,
50045                     listeners : {
50046                         'select': function(c, r, i) {
50047                             if (tb.selectedNode.hasAttribute('data-block')) {
50048                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50049                                 b[c.attrname] = r.get('val');
50050                                 b.updateElement(tb.selectedNode);
50051                                 editorcore.syncValue();
50052                                 return;
50053                             }
50054                             
50055                             if (c.stylename) {
50056                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50057                                 editorcore.syncValue();
50058                                 return;
50059                             }
50060                             if (r === false) {
50061                                 tb.selectedNode.removeAttribute(c.attrname);
50062                                 editorcore.syncValue();
50063                                 return;
50064                             }
50065                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50066                             editorcore.syncValue();
50067                         }
50068                     }
50069
50070                 }));
50071                 continue;
50072                     
50073                  
50074                 /*
50075                 tb.addField( new Roo.form.TextField({
50076                     name: i,
50077                     width: 100,
50078                     //allowBlank:false,
50079                     value: ''
50080                 }));
50081                 continue;
50082                 */
50083             }
50084             tb.addField( new Roo.form.TextField({
50085                 name: '-roo-edit-' + tlist[i].name,
50086                 attrname : tlist[i].name,
50087                 
50088                 width: item.width,
50089                 //allowBlank:true,
50090                 value: '',
50091                 listeners: {
50092                     'change' : function(f, nv, ov) {
50093                         
50094                         if (tb.selectedNode.hasAttribute('data-block')) {
50095                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50096                             b[f.attrname] = nv;
50097                             b.updateElement(tb.selectedNode);
50098                             editorcore.syncValue();
50099                             return;
50100                         }
50101                         
50102                         tb.selectedNode.setAttribute(f.attrname, nv);
50103                         editorcore.syncValue();
50104                     }
50105                 }
50106             }));
50107              
50108         }
50109         
50110         var _this = this;
50111         
50112         if(nm == 'BODY'){
50113             tb.addSeparator();
50114         
50115             tb.addButton( {
50116                 text: 'Stylesheets',
50117
50118                 listeners : {
50119                     click : function ()
50120                     {
50121                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50122                     }
50123                 }
50124             });
50125         }
50126         
50127         tb.addFill();
50128         tb.addButton({
50129             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50130     
50131             listeners : {
50132                 click : function ()
50133                 {
50134                     var sn = tb.selectedNode;
50135                     if (block) {
50136                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50137                         
50138                     }
50139                     if (!sn) {
50140                         return;
50141                     }
50142                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50143                     if (sn.hasAttribute('data-block')) {
50144                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50145                         sn.parentNode.removeChild(sn);
50146                         
50147                     } else if (sn && sn.tagName != 'BODY') {
50148                         // remove and keep parents.
50149                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50150                         a.removeTag(sn);
50151                     }
50152                     
50153                     
50154                     var range = editorcore.createRange();
50155         
50156                     range.setStart(stn,0);
50157                     range.setEnd(stn,0); 
50158                     var selection = editorcore.getSelection();
50159                     selection.removeAllRanges();
50160                     selection.addRange(range);
50161                     
50162                     
50163                     //_this.updateToolbar(null, null, pn);
50164                     _this.updateToolbar(null, null, null);
50165                     _this.updateFooter(false);
50166                     
50167                 }
50168             }
50169             
50170                     
50171                 
50172             
50173         });
50174         
50175         
50176         tb.el.on('click', function(e){
50177             e.preventDefault(); // what does this do?
50178         });
50179         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50180         tb.el.hide();
50181         
50182         // dont need to disable them... as they will get hidden
50183         return tb;
50184          
50185         
50186     },
50187     buildFooter : function()
50188     {
50189         
50190         var fel = this.editor.wrap.createChild();
50191         this.footer = new Roo.Toolbar(fel);
50192         // toolbar has scrolly on left / right?
50193         var footDisp= new Roo.Toolbar.Fill();
50194         var _t = this;
50195         this.footer.add(
50196             {
50197                 text : '&lt;',
50198                 xtype: 'Button',
50199                 handler : function() {
50200                     _t.footDisp.scrollTo('left',0,true)
50201                 }
50202             }
50203         );
50204         this.footer.add( footDisp );
50205         this.footer.add( 
50206             {
50207                 text : '&gt;',
50208                 xtype: 'Button',
50209                 handler : function() {
50210                     // no animation..
50211                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50212                 }
50213             }
50214         );
50215         var fel = Roo.get(footDisp.el);
50216         fel.addClass('x-editor-context');
50217         this.footDispWrap = fel; 
50218         this.footDispWrap.overflow  = 'hidden';
50219         
50220         this.footDisp = fel.createChild();
50221         this.footDispWrap.on('click', this.onContextClick, this)
50222         
50223         
50224     },
50225     // when the footer contect changes
50226     onContextClick : function (ev,dom)
50227     {
50228         ev.preventDefault();
50229         var  cn = dom.className;
50230         //Roo.log(cn);
50231         if (!cn.match(/x-ed-loc-/)) {
50232             return;
50233         }
50234         var n = cn.split('-').pop();
50235         var ans = this.footerEls;
50236         var sel = ans[n];
50237         
50238         this.editorcore.selectNode(sel);
50239         
50240         
50241         this.updateToolbar(null, null, sel);
50242         
50243         
50244     }
50245     
50246     
50247     
50248     
50249     
50250 });
50251
50252
50253
50254
50255
50256 /*
50257  * Based on:
50258  * Ext JS Library 1.1.1
50259  * Copyright(c) 2006-2007, Ext JS, LLC.
50260  *
50261  * Originally Released Under LGPL - original licence link has changed is not relivant.
50262  *
50263  * Fork - LGPL
50264  * <script type="text/javascript">
50265  */
50266  
50267 /**
50268  * @class Roo.form.BasicForm
50269  * @extends Roo.util.Observable
50270  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50271  * @constructor
50272  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50273  * @param {Object} config Configuration options
50274  */
50275 Roo.form.BasicForm = function(el, config){
50276     this.allItems = [];
50277     this.childForms = [];
50278     Roo.apply(this, config);
50279     /*
50280      * The Roo.form.Field items in this form.
50281      * @type MixedCollection
50282      */
50283      
50284      
50285     this.items = new Roo.util.MixedCollection(false, function(o){
50286         return o.id || (o.id = Roo.id());
50287     });
50288     this.addEvents({
50289         /**
50290          * @event beforeaction
50291          * Fires before any action is performed. Return false to cancel the action.
50292          * @param {Form} this
50293          * @param {Action} action The action to be performed
50294          */
50295         beforeaction: true,
50296         /**
50297          * @event actionfailed
50298          * Fires when an action fails.
50299          * @param {Form} this
50300          * @param {Action} action The action that failed
50301          */
50302         actionfailed : true,
50303         /**
50304          * @event actioncomplete
50305          * Fires when an action is completed.
50306          * @param {Form} this
50307          * @param {Action} action The action that completed
50308          */
50309         actioncomplete : true
50310     });
50311     if(el){
50312         this.initEl(el);
50313     }
50314     Roo.form.BasicForm.superclass.constructor.call(this);
50315     
50316     Roo.form.BasicForm.popover.apply();
50317 };
50318
50319 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50320     /**
50321      * @cfg {String} method
50322      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50323      */
50324     /**
50325      * @cfg {DataReader} reader
50326      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50327      * This is optional as there is built-in support for processing JSON.
50328      */
50329     /**
50330      * @cfg {DataReader} errorReader
50331      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50332      * This is completely optional as there is built-in support for processing JSON.
50333      */
50334     /**
50335      * @cfg {String} url
50336      * The URL to use for form actions if one isn't supplied in the action options.
50337      */
50338     /**
50339      * @cfg {Boolean} fileUpload
50340      * Set to true if this form is a file upload.
50341      */
50342      
50343     /**
50344      * @cfg {Object} baseParams
50345      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50346      */
50347      /**
50348      
50349     /**
50350      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50351      */
50352     timeout: 30,
50353
50354     // private
50355     activeAction : null,
50356
50357     /**
50358      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50359      * or setValues() data instead of when the form was first created.
50360      */
50361     trackResetOnLoad : false,
50362     
50363     
50364     /**
50365      * childForms - used for multi-tab forms
50366      * @type {Array}
50367      */
50368     childForms : false,
50369     
50370     /**
50371      * allItems - full list of fields.
50372      * @type {Array}
50373      */
50374     allItems : false,
50375     
50376     /**
50377      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50378      * element by passing it or its id or mask the form itself by passing in true.
50379      * @type Mixed
50380      */
50381     waitMsgTarget : false,
50382     
50383     /**
50384      * @type Boolean
50385      */
50386     disableMask : false,
50387     
50388     /**
50389      * @cfg {Boolean} errorMask (true|false) default false
50390      */
50391     errorMask : false,
50392     
50393     /**
50394      * @cfg {Number} maskOffset Default 100
50395      */
50396     maskOffset : 100,
50397
50398     // private
50399     initEl : function(el){
50400         this.el = Roo.get(el);
50401         this.id = this.el.id || Roo.id();
50402         this.el.on('submit', this.onSubmit, this);
50403         this.el.addClass('x-form');
50404     },
50405
50406     // private
50407     onSubmit : function(e){
50408         e.stopEvent();
50409     },
50410
50411     /**
50412      * Returns true if client-side validation on the form is successful.
50413      * @return Boolean
50414      */
50415     isValid : function(){
50416         var valid = true;
50417         var target = false;
50418         this.items.each(function(f){
50419             if(f.validate()){
50420                 return;
50421             }
50422             
50423             valid = false;
50424                 
50425             if(!target && f.el.isVisible(true)){
50426                 target = f;
50427             }
50428         });
50429         
50430         if(this.errorMask && !valid){
50431             Roo.form.BasicForm.popover.mask(this, target);
50432         }
50433         
50434         return valid;
50435     },
50436     /**
50437      * Returns array of invalid form fields.
50438      * @return Array
50439      */
50440     
50441     invalidFields : function()
50442     {
50443         var ret = [];
50444         this.items.each(function(f){
50445             if(f.validate()){
50446                 return;
50447             }
50448             ret.push(f);
50449             
50450         });
50451         
50452         return ret;
50453     },
50454     
50455     
50456     /**
50457      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50458      * @return Boolean
50459      */
50460     isDirty : function(){
50461         var dirty = false;
50462         this.items.each(function(f){
50463            if(f.isDirty()){
50464                dirty = true;
50465                return false;
50466            }
50467         });
50468         return dirty;
50469     },
50470     
50471     /**
50472      * Returns true if any fields in this form have changed since their original load. (New version)
50473      * @return Boolean
50474      */
50475     
50476     hasChanged : function()
50477     {
50478         var dirty = false;
50479         this.items.each(function(f){
50480            if(f.hasChanged()){
50481                dirty = true;
50482                return false;
50483            }
50484         });
50485         return dirty;
50486         
50487     },
50488     /**
50489      * Resets all hasChanged to 'false' -
50490      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50491      * So hasChanged storage is only to be used for this purpose
50492      * @return Boolean
50493      */
50494     resetHasChanged : function()
50495     {
50496         this.items.each(function(f){
50497            f.resetHasChanged();
50498         });
50499         
50500     },
50501     
50502     
50503     /**
50504      * Performs a predefined action (submit or load) or custom actions you define on this form.
50505      * @param {String} actionName The name of the action type
50506      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50507      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50508      * accept other config options):
50509      * <pre>
50510 Property          Type             Description
50511 ----------------  ---------------  ----------------------------------------------------------------------------------
50512 url               String           The url for the action (defaults to the form's url)
50513 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50514 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50515 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50516                                    validate the form on the client (defaults to false)
50517      * </pre>
50518      * @return {BasicForm} this
50519      */
50520     doAction : function(action, options){
50521         if(typeof action == 'string'){
50522             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50523         }
50524         if(this.fireEvent('beforeaction', this, action) !== false){
50525             this.beforeAction(action);
50526             action.run.defer(100, action);
50527         }
50528         return this;
50529     },
50530
50531     /**
50532      * Shortcut to do a submit action.
50533      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50534      * @return {BasicForm} this
50535      */
50536     submit : function(options){
50537         this.doAction('submit', options);
50538         return this;
50539     },
50540
50541     /**
50542      * Shortcut to do a load action.
50543      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50544      * @return {BasicForm} this
50545      */
50546     load : function(options){
50547         this.doAction('load', options);
50548         return this;
50549     },
50550
50551     /**
50552      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50553      * @param {Record} record The record to edit
50554      * @return {BasicForm} this
50555      */
50556     updateRecord : function(record){
50557         record.beginEdit();
50558         var fs = record.fields;
50559         fs.each(function(f){
50560             var field = this.findField(f.name);
50561             if(field){
50562                 record.set(f.name, field.getValue());
50563             }
50564         }, this);
50565         record.endEdit();
50566         return this;
50567     },
50568
50569     /**
50570      * Loads an Roo.data.Record into this form.
50571      * @param {Record} record The record to load
50572      * @return {BasicForm} this
50573      */
50574     loadRecord : function(record){
50575         this.setValues(record.data);
50576         return this;
50577     },
50578
50579     // private
50580     beforeAction : function(action){
50581         var o = action.options;
50582         
50583         if(!this.disableMask) {
50584             if(this.waitMsgTarget === true){
50585                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50586             }else if(this.waitMsgTarget){
50587                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50588                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50589             }else {
50590                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50591             }
50592         }
50593         
50594          
50595     },
50596
50597     // private
50598     afterAction : function(action, success){
50599         this.activeAction = null;
50600         var o = action.options;
50601         
50602         if(!this.disableMask) {
50603             if(this.waitMsgTarget === true){
50604                 this.el.unmask();
50605             }else if(this.waitMsgTarget){
50606                 this.waitMsgTarget.unmask();
50607             }else{
50608                 Roo.MessageBox.updateProgress(1);
50609                 Roo.MessageBox.hide();
50610             }
50611         }
50612         
50613         if(success){
50614             if(o.reset){
50615                 this.reset();
50616             }
50617             Roo.callback(o.success, o.scope, [this, action]);
50618             this.fireEvent('actioncomplete', this, action);
50619             
50620         }else{
50621             
50622             // failure condition..
50623             // we have a scenario where updates need confirming.
50624             // eg. if a locking scenario exists..
50625             // we look for { errors : { needs_confirm : true }} in the response.
50626             if (
50627                 (typeof(action.result) != 'undefined')  &&
50628                 (typeof(action.result.errors) != 'undefined')  &&
50629                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50630            ){
50631                 var _t = this;
50632                 Roo.MessageBox.confirm(
50633                     "Change requires confirmation",
50634                     action.result.errorMsg,
50635                     function(r) {
50636                         if (r != 'yes') {
50637                             return;
50638                         }
50639                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50640                     }
50641                     
50642                 );
50643                 
50644                 
50645                 
50646                 return;
50647             }
50648             
50649             Roo.callback(o.failure, o.scope, [this, action]);
50650             // show an error message if no failed handler is set..
50651             if (!this.hasListener('actionfailed')) {
50652                 Roo.MessageBox.alert("Error",
50653                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50654                         action.result.errorMsg :
50655                         "Saving Failed, please check your entries or try again"
50656                 );
50657             }
50658             
50659             this.fireEvent('actionfailed', this, action);
50660         }
50661         
50662     },
50663
50664     /**
50665      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50666      * @param {String} id The value to search for
50667      * @return Field
50668      */
50669     findField : function(id){
50670         var field = this.items.get(id);
50671         if(!field){
50672             this.items.each(function(f){
50673                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50674                     field = f;
50675                     return false;
50676                 }
50677             });
50678         }
50679         return field || null;
50680     },
50681
50682     /**
50683      * Add a secondary form to this one, 
50684      * Used to provide tabbed forms. One form is primary, with hidden values 
50685      * which mirror the elements from the other forms.
50686      * 
50687      * @param {Roo.form.Form} form to add.
50688      * 
50689      */
50690     addForm : function(form)
50691     {
50692        
50693         if (this.childForms.indexOf(form) > -1) {
50694             // already added..
50695             return;
50696         }
50697         this.childForms.push(form);
50698         var n = '';
50699         Roo.each(form.allItems, function (fe) {
50700             
50701             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50702             if (this.findField(n)) { // already added..
50703                 return;
50704             }
50705             var add = new Roo.form.Hidden({
50706                 name : n
50707             });
50708             add.render(this.el);
50709             
50710             this.add( add );
50711         }, this);
50712         
50713     },
50714     /**
50715      * Mark fields in this form invalid in bulk.
50716      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50717      * @return {BasicForm} this
50718      */
50719     markInvalid : function(errors){
50720         if(errors instanceof Array){
50721             for(var i = 0, len = errors.length; i < len; i++){
50722                 var fieldError = errors[i];
50723                 var f = this.findField(fieldError.id);
50724                 if(f){
50725                     f.markInvalid(fieldError.msg);
50726                 }
50727             }
50728         }else{
50729             var field, id;
50730             for(id in errors){
50731                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50732                     field.markInvalid(errors[id]);
50733                 }
50734             }
50735         }
50736         Roo.each(this.childForms || [], function (f) {
50737             f.markInvalid(errors);
50738         });
50739         
50740         return this;
50741     },
50742
50743     /**
50744      * Set values for fields in this form in bulk.
50745      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50746      * @return {BasicForm} this
50747      */
50748     setValues : function(values){
50749         if(values instanceof Array){ // array of objects
50750             for(var i = 0, len = values.length; i < len; i++){
50751                 var v = values[i];
50752                 var f = this.findField(v.id);
50753                 if(f){
50754                     f.setValue(v.value);
50755                     if(this.trackResetOnLoad){
50756                         f.originalValue = f.getValue();
50757                     }
50758                 }
50759             }
50760         }else{ // object hash
50761             var field, id;
50762             for(id in values){
50763                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50764                     
50765                     if (field.setFromData && 
50766                         field.valueField && 
50767                         field.displayField &&
50768                         // combos' with local stores can 
50769                         // be queried via setValue()
50770                         // to set their value..
50771                         (field.store && !field.store.isLocal)
50772                         ) {
50773                         // it's a combo
50774                         var sd = { };
50775                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50776                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50777                         field.setFromData(sd);
50778                         
50779                     } else {
50780                         field.setValue(values[id]);
50781                     }
50782                     
50783                     
50784                     if(this.trackResetOnLoad){
50785                         field.originalValue = field.getValue();
50786                     }
50787                 }
50788             }
50789         }
50790         this.resetHasChanged();
50791         
50792         
50793         Roo.each(this.childForms || [], function (f) {
50794             f.setValues(values);
50795             f.resetHasChanged();
50796         });
50797                 
50798         return this;
50799     },
50800  
50801     /**
50802      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50803      * they are returned as an array.
50804      * @param {Boolean} asString
50805      * @return {Object}
50806      */
50807     getValues : function(asString)
50808     {
50809         if (this.childForms) {
50810             // copy values from the child forms
50811             Roo.each(this.childForms, function (f) {
50812                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50813             }, this);
50814         }
50815         
50816         // use formdata
50817         if (typeof(FormData) != 'undefined' && asString !== true) {
50818             // this relies on a 'recent' version of chrome apparently...
50819             try {
50820                 var fd = (new FormData(this.el.dom)).entries();
50821                 var ret = {};
50822                 var ent = fd.next();
50823                 while (!ent.done) {
50824                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50825                     ent = fd.next();
50826                 };
50827                 return ret;
50828             } catch(e) {
50829                 
50830             }
50831             
50832         }
50833         
50834         
50835         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50836         if(asString === true){
50837             return fs;
50838         }
50839         return Roo.urlDecode(fs);
50840     },
50841     
50842     /**
50843      * Returns the fields in this form as an object with key/value pairs. 
50844      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50845      * Normally this will not return readOnly data 
50846      * @param {Boolean} with_readonly return readonly field data.
50847      * @return {Object}
50848      */
50849     getFieldValues : function(with_readonly)
50850     {
50851         if (this.childForms) {
50852             // copy values from the child forms
50853             // should this call getFieldValues - probably not as we do not currently copy
50854             // hidden fields when we generate..
50855             Roo.each(this.childForms, function (f) {
50856                 this.setValues(f.getFieldValues());
50857             }, this);
50858         }
50859         
50860         var ret = {};
50861         this.items.each(function(f){
50862             
50863             if (f.readOnly && with_readonly !== true) {
50864                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50865                         // if a subform contains a copy of them.
50866                         // if you have subforms with the same editable data, you will need to copy the data back
50867                         // and forth.
50868             }
50869             
50870             if (!f.getName()) {
50871                 return;
50872             }
50873             var v = f.getValue();
50874             if (f.inputType =='radio') {
50875                 if (typeof(ret[f.getName()]) == 'undefined') {
50876                     ret[f.getName()] = ''; // empty..
50877                 }
50878                 
50879                 if (!f.el.dom.checked) {
50880                     return;
50881                     
50882                 }
50883                 v = f.el.dom.value;
50884                 
50885             }
50886             
50887             // not sure if this supported any more..
50888             if ((typeof(v) == 'object') && f.getRawValue) {
50889                 v = f.getRawValue() ; // dates..
50890             }
50891             // combo boxes where name != hiddenName...
50892             if (f.name != f.getName()) {
50893                 ret[f.name] = f.getRawValue();
50894             }
50895             ret[f.getName()] = v;
50896         });
50897         
50898         return ret;
50899     },
50900
50901     /**
50902      * Clears all invalid messages in this form.
50903      * @return {BasicForm} this
50904      */
50905     clearInvalid : function(){
50906         this.items.each(function(f){
50907            f.clearInvalid();
50908         });
50909         
50910         Roo.each(this.childForms || [], function (f) {
50911             f.clearInvalid();
50912         });
50913         
50914         
50915         return this;
50916     },
50917
50918     /**
50919      * Resets this form.
50920      * @return {BasicForm} this
50921      */
50922     reset : function(){
50923         this.items.each(function(f){
50924             f.reset();
50925         });
50926         
50927         Roo.each(this.childForms || [], function (f) {
50928             f.reset();
50929         });
50930         this.resetHasChanged();
50931         
50932         return this;
50933     },
50934
50935     /**
50936      * Add Roo.form components to this form.
50937      * @param {Field} field1
50938      * @param {Field} field2 (optional)
50939      * @param {Field} etc (optional)
50940      * @return {BasicForm} this
50941      */
50942     add : function(){
50943         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50944         return this;
50945     },
50946
50947
50948     /**
50949      * Removes a field from the items collection (does NOT remove its markup).
50950      * @param {Field} field
50951      * @return {BasicForm} this
50952      */
50953     remove : function(field){
50954         this.items.remove(field);
50955         return this;
50956     },
50957
50958     /**
50959      * Looks at the fields in this form, checks them for an id attribute,
50960      * and calls applyTo on the existing dom element with that id.
50961      * @return {BasicForm} this
50962      */
50963     render : function(){
50964         this.items.each(function(f){
50965             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50966                 f.applyTo(f.id);
50967             }
50968         });
50969         return this;
50970     },
50971
50972     /**
50973      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50974      * @param {Object} values
50975      * @return {BasicForm} this
50976      */
50977     applyToFields : function(o){
50978         this.items.each(function(f){
50979            Roo.apply(f, o);
50980         });
50981         return this;
50982     },
50983
50984     /**
50985      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50986      * @param {Object} values
50987      * @return {BasicForm} this
50988      */
50989     applyIfToFields : function(o){
50990         this.items.each(function(f){
50991            Roo.applyIf(f, o);
50992         });
50993         return this;
50994     }
50995 });
50996
50997 // back compat
50998 Roo.BasicForm = Roo.form.BasicForm;
50999
51000 Roo.apply(Roo.form.BasicForm, {
51001     
51002     popover : {
51003         
51004         padding : 5,
51005         
51006         isApplied : false,
51007         
51008         isMasked : false,
51009         
51010         form : false,
51011         
51012         target : false,
51013         
51014         intervalID : false,
51015         
51016         maskEl : false,
51017         
51018         apply : function()
51019         {
51020             if(this.isApplied){
51021                 return;
51022             }
51023             
51024             this.maskEl = {
51025                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51026                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51027                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51028                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51029             };
51030             
51031             this.maskEl.top.enableDisplayMode("block");
51032             this.maskEl.left.enableDisplayMode("block");
51033             this.maskEl.bottom.enableDisplayMode("block");
51034             this.maskEl.right.enableDisplayMode("block");
51035             
51036             Roo.get(document.body).on('click', function(){
51037                 this.unmask();
51038             }, this);
51039             
51040             Roo.get(document.body).on('touchstart', function(){
51041                 this.unmask();
51042             }, this);
51043             
51044             this.isApplied = true
51045         },
51046         
51047         mask : function(form, target)
51048         {
51049             this.form = form;
51050             
51051             this.target = target;
51052             
51053             if(!this.form.errorMask || !target.el){
51054                 return;
51055             }
51056             
51057             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51058             
51059             var ot = this.target.el.calcOffsetsTo(scrollable);
51060             
51061             var scrollTo = ot[1] - this.form.maskOffset;
51062             
51063             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51064             
51065             scrollable.scrollTo('top', scrollTo);
51066             
51067             var el = this.target.wrap || this.target.el;
51068             
51069             var box = el.getBox();
51070             
51071             this.maskEl.top.setStyle('position', 'absolute');
51072             this.maskEl.top.setStyle('z-index', 10000);
51073             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51074             this.maskEl.top.setLeft(0);
51075             this.maskEl.top.setTop(0);
51076             this.maskEl.top.show();
51077             
51078             this.maskEl.left.setStyle('position', 'absolute');
51079             this.maskEl.left.setStyle('z-index', 10000);
51080             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51081             this.maskEl.left.setLeft(0);
51082             this.maskEl.left.setTop(box.y - this.padding);
51083             this.maskEl.left.show();
51084
51085             this.maskEl.bottom.setStyle('position', 'absolute');
51086             this.maskEl.bottom.setStyle('z-index', 10000);
51087             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51088             this.maskEl.bottom.setLeft(0);
51089             this.maskEl.bottom.setTop(box.bottom + this.padding);
51090             this.maskEl.bottom.show();
51091
51092             this.maskEl.right.setStyle('position', 'absolute');
51093             this.maskEl.right.setStyle('z-index', 10000);
51094             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51095             this.maskEl.right.setLeft(box.right + this.padding);
51096             this.maskEl.right.setTop(box.y - this.padding);
51097             this.maskEl.right.show();
51098
51099             this.intervalID = window.setInterval(function() {
51100                 Roo.form.BasicForm.popover.unmask();
51101             }, 10000);
51102
51103             window.onwheel = function(){ return false;};
51104             
51105             (function(){ this.isMasked = true; }).defer(500, this);
51106             
51107         },
51108         
51109         unmask : function()
51110         {
51111             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51112                 return;
51113             }
51114             
51115             this.maskEl.top.setStyle('position', 'absolute');
51116             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51117             this.maskEl.top.hide();
51118
51119             this.maskEl.left.setStyle('position', 'absolute');
51120             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51121             this.maskEl.left.hide();
51122
51123             this.maskEl.bottom.setStyle('position', 'absolute');
51124             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51125             this.maskEl.bottom.hide();
51126
51127             this.maskEl.right.setStyle('position', 'absolute');
51128             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51129             this.maskEl.right.hide();
51130             
51131             window.onwheel = function(){ return true;};
51132             
51133             if(this.intervalID){
51134                 window.clearInterval(this.intervalID);
51135                 this.intervalID = false;
51136             }
51137             
51138             this.isMasked = false;
51139             
51140         }
51141         
51142     }
51143     
51144 });/*
51145  * Based on:
51146  * Ext JS Library 1.1.1
51147  * Copyright(c) 2006-2007, Ext JS, LLC.
51148  *
51149  * Originally Released Under LGPL - original licence link has changed is not relivant.
51150  *
51151  * Fork - LGPL
51152  * <script type="text/javascript">
51153  */
51154
51155 /**
51156  * @class Roo.form.Form
51157  * @extends Roo.form.BasicForm
51158  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51159  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51160  * @constructor
51161  * @param {Object} config Configuration options
51162  */
51163 Roo.form.Form = function(config){
51164     var xitems =  [];
51165     if (config.items) {
51166         xitems = config.items;
51167         delete config.items;
51168     }
51169    
51170     
51171     Roo.form.Form.superclass.constructor.call(this, null, config);
51172     this.url = this.url || this.action;
51173     if(!this.root){
51174         this.root = new Roo.form.Layout(Roo.applyIf({
51175             id: Roo.id()
51176         }, config));
51177     }
51178     this.active = this.root;
51179     /**
51180      * Array of all the buttons that have been added to this form via {@link addButton}
51181      * @type Array
51182      */
51183     this.buttons = [];
51184     this.allItems = [];
51185     this.addEvents({
51186         /**
51187          * @event clientvalidation
51188          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51189          * @param {Form} this
51190          * @param {Boolean} valid true if the form has passed client-side validation
51191          */
51192         clientvalidation: true,
51193         /**
51194          * @event rendered
51195          * Fires when the form is rendered
51196          * @param {Roo.form.Form} form
51197          */
51198         rendered : true
51199     });
51200     
51201     if (this.progressUrl) {
51202             // push a hidden field onto the list of fields..
51203             this.addxtype( {
51204                     xns: Roo.form, 
51205                     xtype : 'Hidden', 
51206                     name : 'UPLOAD_IDENTIFIER' 
51207             });
51208         }
51209         
51210     
51211     Roo.each(xitems, this.addxtype, this);
51212     
51213 };
51214
51215 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51216      /**
51217      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51218      */
51219     
51220     /**
51221      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51222      */
51223     /**
51224      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51225      */
51226     /**
51227      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51228      */
51229     buttonAlign:'center',
51230
51231     /**
51232      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51233      */
51234     minButtonWidth:75,
51235
51236     /**
51237      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51238      * This property cascades to child containers if not set.
51239      */
51240     labelAlign:'left',
51241
51242     /**
51243      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51244      * fires a looping event with that state. This is required to bind buttons to the valid
51245      * state using the config value formBind:true on the button.
51246      */
51247     monitorValid : false,
51248
51249     /**
51250      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51251      */
51252     monitorPoll : 200,
51253     
51254     /**
51255      * @cfg {String} progressUrl - Url to return progress data 
51256      */
51257     
51258     progressUrl : false,
51259     /**
51260      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51261      * sending a formdata with extra parameters - eg uploaded elements.
51262      */
51263     
51264     formData : false,
51265     
51266     /**
51267      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51268      * fields are added and the column is closed. If no fields are passed the column remains open
51269      * until end() is called.
51270      * @param {Object} config The config to pass to the column
51271      * @param {Field} field1 (optional)
51272      * @param {Field} field2 (optional)
51273      * @param {Field} etc (optional)
51274      * @return Column The column container object
51275      */
51276     column : function(c){
51277         var col = new Roo.form.Column(c);
51278         this.start(col);
51279         if(arguments.length > 1){ // duplicate code required because of Opera
51280             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51281             this.end();
51282         }
51283         return col;
51284     },
51285
51286     /**
51287      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51288      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51289      * until end() is called.
51290      * @param {Object} config The config to pass to the fieldset
51291      * @param {Field} field1 (optional)
51292      * @param {Field} field2 (optional)
51293      * @param {Field} etc (optional)
51294      * @return FieldSet The fieldset container object
51295      */
51296     fieldset : function(c){
51297         var fs = new Roo.form.FieldSet(c);
51298         this.start(fs);
51299         if(arguments.length > 1){ // duplicate code required because of Opera
51300             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51301             this.end();
51302         }
51303         return fs;
51304     },
51305
51306     /**
51307      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51308      * fields are added and the container is closed. If no fields are passed the container remains open
51309      * until end() is called.
51310      * @param {Object} config The config to pass to the Layout
51311      * @param {Field} field1 (optional)
51312      * @param {Field} field2 (optional)
51313      * @param {Field} etc (optional)
51314      * @return Layout The container object
51315      */
51316     container : function(c){
51317         var l = new Roo.form.Layout(c);
51318         this.start(l);
51319         if(arguments.length > 1){ // duplicate code required because of Opera
51320             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51321             this.end();
51322         }
51323         return l;
51324     },
51325
51326     /**
51327      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51328      * @param {Object} container A Roo.form.Layout or subclass of Layout
51329      * @return {Form} this
51330      */
51331     start : function(c){
51332         // cascade label info
51333         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51334         this.active.stack.push(c);
51335         c.ownerCt = this.active;
51336         this.active = c;
51337         return this;
51338     },
51339
51340     /**
51341      * Closes the current open container
51342      * @return {Form} this
51343      */
51344     end : function(){
51345         if(this.active == this.root){
51346             return this;
51347         }
51348         this.active = this.active.ownerCt;
51349         return this;
51350     },
51351
51352     /**
51353      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51354      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51355      * as the label of the field.
51356      * @param {Field} field1
51357      * @param {Field} field2 (optional)
51358      * @param {Field} etc. (optional)
51359      * @return {Form} this
51360      */
51361     add : function(){
51362         this.active.stack.push.apply(this.active.stack, arguments);
51363         this.allItems.push.apply(this.allItems,arguments);
51364         var r = [];
51365         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51366             if(a[i].isFormField){
51367                 r.push(a[i]);
51368             }
51369         }
51370         if(r.length > 0){
51371             Roo.form.Form.superclass.add.apply(this, r);
51372         }
51373         return this;
51374     },
51375     
51376
51377     
51378     
51379     
51380      /**
51381      * Find any element that has been added to a form, using it's ID or name
51382      * This can include framesets, columns etc. along with regular fields..
51383      * @param {String} id - id or name to find.
51384      
51385      * @return {Element} e - or false if nothing found.
51386      */
51387     findbyId : function(id)
51388     {
51389         var ret = false;
51390         if (!id) {
51391             return ret;
51392         }
51393         Roo.each(this.allItems, function(f){
51394             if (f.id == id || f.name == id ){
51395                 ret = f;
51396                 return false;
51397             }
51398         });
51399         return ret;
51400     },
51401
51402     
51403     
51404     /**
51405      * Render this form into the passed container. This should only be called once!
51406      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51407      * @return {Form} this
51408      */
51409     render : function(ct)
51410     {
51411         
51412         
51413         
51414         ct = Roo.get(ct);
51415         var o = this.autoCreate || {
51416             tag: 'form',
51417             method : this.method || 'POST',
51418             id : this.id || Roo.id()
51419         };
51420         this.initEl(ct.createChild(o));
51421
51422         this.root.render(this.el);
51423         
51424        
51425              
51426         this.items.each(function(f){
51427             f.render('x-form-el-'+f.id);
51428         });
51429
51430         if(this.buttons.length > 0){
51431             // tables are required to maintain order and for correct IE layout
51432             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51433                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51434                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51435             }}, null, true);
51436             var tr = tb.getElementsByTagName('tr')[0];
51437             for(var i = 0, len = this.buttons.length; i < len; i++) {
51438                 var b = this.buttons[i];
51439                 var td = document.createElement('td');
51440                 td.className = 'x-form-btn-td';
51441                 b.render(tr.appendChild(td));
51442             }
51443         }
51444         if(this.monitorValid){ // initialize after render
51445             this.startMonitoring();
51446         }
51447         this.fireEvent('rendered', this);
51448         return this;
51449     },
51450
51451     /**
51452      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51453      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51454      * object or a valid Roo.DomHelper element config
51455      * @param {Function} handler The function called when the button is clicked
51456      * @param {Object} scope (optional) The scope of the handler function
51457      * @return {Roo.Button}
51458      */
51459     addButton : function(config, handler, scope){
51460         var bc = {
51461             handler: handler,
51462             scope: scope,
51463             minWidth: this.minButtonWidth,
51464             hideParent:true
51465         };
51466         if(typeof config == "string"){
51467             bc.text = config;
51468         }else{
51469             Roo.apply(bc, config);
51470         }
51471         var btn = new Roo.Button(null, bc);
51472         this.buttons.push(btn);
51473         return btn;
51474     },
51475
51476      /**
51477      * Adds a series of form elements (using the xtype property as the factory method.
51478      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51479      * @param {Object} config 
51480      */
51481     
51482     addxtype : function()
51483     {
51484         var ar = Array.prototype.slice.call(arguments, 0);
51485         var ret = false;
51486         for(var i = 0; i < ar.length; i++) {
51487             if (!ar[i]) {
51488                 continue; // skip -- if this happends something invalid got sent, we 
51489                 // should ignore it, as basically that interface element will not show up
51490                 // and that should be pretty obvious!!
51491             }
51492             
51493             if (Roo.form[ar[i].xtype]) {
51494                 ar[i].form = this;
51495                 var fe = Roo.factory(ar[i], Roo.form);
51496                 if (!ret) {
51497                     ret = fe;
51498                 }
51499                 fe.form = this;
51500                 if (fe.store) {
51501                     fe.store.form = this;
51502                 }
51503                 if (fe.isLayout) {  
51504                          
51505                     this.start(fe);
51506                     this.allItems.push(fe);
51507                     if (fe.items && fe.addxtype) {
51508                         fe.addxtype.apply(fe, fe.items);
51509                         delete fe.items;
51510                     }
51511                      this.end();
51512                     continue;
51513                 }
51514                 
51515                 
51516                  
51517                 this.add(fe);
51518               //  console.log('adding ' + ar[i].xtype);
51519             }
51520             if (ar[i].xtype == 'Button') {  
51521                 //console.log('adding button');
51522                 //console.log(ar[i]);
51523                 this.addButton(ar[i]);
51524                 this.allItems.push(fe);
51525                 continue;
51526             }
51527             
51528             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51529                 alert('end is not supported on xtype any more, use items');
51530             //    this.end();
51531             //    //console.log('adding end');
51532             }
51533             
51534         }
51535         return ret;
51536     },
51537     
51538     /**
51539      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51540      * option "monitorValid"
51541      */
51542     startMonitoring : function(){
51543         if(!this.bound){
51544             this.bound = true;
51545             Roo.TaskMgr.start({
51546                 run : this.bindHandler,
51547                 interval : this.monitorPoll || 200,
51548                 scope: this
51549             });
51550         }
51551     },
51552
51553     /**
51554      * Stops monitoring of the valid state of this form
51555      */
51556     stopMonitoring : function(){
51557         this.bound = false;
51558     },
51559
51560     // private
51561     bindHandler : function(){
51562         if(!this.bound){
51563             return false; // stops binding
51564         }
51565         var valid = true;
51566         this.items.each(function(f){
51567             if(!f.isValid(true)){
51568                 valid = false;
51569                 return false;
51570             }
51571         });
51572         for(var i = 0, len = this.buttons.length; i < len; i++){
51573             var btn = this.buttons[i];
51574             if(btn.formBind === true && btn.disabled === valid){
51575                 btn.setDisabled(!valid);
51576             }
51577         }
51578         this.fireEvent('clientvalidation', this, valid);
51579     }
51580     
51581     
51582     
51583     
51584     
51585     
51586     
51587     
51588 });
51589
51590
51591 // back compat
51592 Roo.Form = Roo.form.Form;
51593 /*
51594  * Based on:
51595  * Ext JS Library 1.1.1
51596  * Copyright(c) 2006-2007, Ext JS, LLC.
51597  *
51598  * Originally Released Under LGPL - original licence link has changed is not relivant.
51599  *
51600  * Fork - LGPL
51601  * <script type="text/javascript">
51602  */
51603
51604 // as we use this in bootstrap.
51605 Roo.namespace('Roo.form');
51606  /**
51607  * @class Roo.form.Action
51608  * Internal Class used to handle form actions
51609  * @constructor
51610  * @param {Roo.form.BasicForm} el The form element or its id
51611  * @param {Object} config Configuration options
51612  */
51613
51614  
51615  
51616 // define the action interface
51617 Roo.form.Action = function(form, options){
51618     this.form = form;
51619     this.options = options || {};
51620 };
51621 /**
51622  * Client Validation Failed
51623  * @const 
51624  */
51625 Roo.form.Action.CLIENT_INVALID = 'client';
51626 /**
51627  * Server Validation Failed
51628  * @const 
51629  */
51630 Roo.form.Action.SERVER_INVALID = 'server';
51631  /**
51632  * Connect to Server Failed
51633  * @const 
51634  */
51635 Roo.form.Action.CONNECT_FAILURE = 'connect';
51636 /**
51637  * Reading Data from Server Failed
51638  * @const 
51639  */
51640 Roo.form.Action.LOAD_FAILURE = 'load';
51641
51642 Roo.form.Action.prototype = {
51643     type : 'default',
51644     failureType : undefined,
51645     response : undefined,
51646     result : undefined,
51647
51648     // interface method
51649     run : function(options){
51650
51651     },
51652
51653     // interface method
51654     success : function(response){
51655
51656     },
51657
51658     // interface method
51659     handleResponse : function(response){
51660
51661     },
51662
51663     // default connection failure
51664     failure : function(response){
51665         
51666         this.response = response;
51667         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51668         this.form.afterAction(this, false);
51669     },
51670
51671     processResponse : function(response){
51672         this.response = response;
51673         if(!response.responseText){
51674             return true;
51675         }
51676         this.result = this.handleResponse(response);
51677         return this.result;
51678     },
51679
51680     // utility functions used internally
51681     getUrl : function(appendParams){
51682         var url = this.options.url || this.form.url || this.form.el.dom.action;
51683         if(appendParams){
51684             var p = this.getParams();
51685             if(p){
51686                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51687             }
51688         }
51689         return url;
51690     },
51691
51692     getMethod : function(){
51693         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51694     },
51695
51696     getParams : function(){
51697         var bp = this.form.baseParams;
51698         var p = this.options.params;
51699         if(p){
51700             if(typeof p == "object"){
51701                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51702             }else if(typeof p == 'string' && bp){
51703                 p += '&' + Roo.urlEncode(bp);
51704             }
51705         }else if(bp){
51706             p = Roo.urlEncode(bp);
51707         }
51708         return p;
51709     },
51710
51711     createCallback : function(){
51712         return {
51713             success: this.success,
51714             failure: this.failure,
51715             scope: this,
51716             timeout: (this.form.timeout*1000),
51717             upload: this.form.fileUpload ? this.success : undefined
51718         };
51719     }
51720 };
51721
51722 Roo.form.Action.Submit = function(form, options){
51723     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51724 };
51725
51726 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51727     type : 'submit',
51728
51729     haveProgress : false,
51730     uploadComplete : false,
51731     
51732     // uploadProgress indicator.
51733     uploadProgress : function()
51734     {
51735         if (!this.form.progressUrl) {
51736             return;
51737         }
51738         
51739         if (!this.haveProgress) {
51740             Roo.MessageBox.progress("Uploading", "Uploading");
51741         }
51742         if (this.uploadComplete) {
51743            Roo.MessageBox.hide();
51744            return;
51745         }
51746         
51747         this.haveProgress = true;
51748    
51749         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51750         
51751         var c = new Roo.data.Connection();
51752         c.request({
51753             url : this.form.progressUrl,
51754             params: {
51755                 id : uid
51756             },
51757             method: 'GET',
51758             success : function(req){
51759                //console.log(data);
51760                 var rdata = false;
51761                 var edata;
51762                 try  {
51763                    rdata = Roo.decode(req.responseText)
51764                 } catch (e) {
51765                     Roo.log("Invalid data from server..");
51766                     Roo.log(edata);
51767                     return;
51768                 }
51769                 if (!rdata || !rdata.success) {
51770                     Roo.log(rdata);
51771                     Roo.MessageBox.alert(Roo.encode(rdata));
51772                     return;
51773                 }
51774                 var data = rdata.data;
51775                 
51776                 if (this.uploadComplete) {
51777                    Roo.MessageBox.hide();
51778                    return;
51779                 }
51780                    
51781                 if (data){
51782                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51783                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51784                     );
51785                 }
51786                 this.uploadProgress.defer(2000,this);
51787             },
51788        
51789             failure: function(data) {
51790                 Roo.log('progress url failed ');
51791                 Roo.log(data);
51792             },
51793             scope : this
51794         });
51795            
51796     },
51797     
51798     
51799     run : function()
51800     {
51801         // run get Values on the form, so it syncs any secondary forms.
51802         this.form.getValues();
51803         
51804         var o = this.options;
51805         var method = this.getMethod();
51806         var isPost = method == 'POST';
51807         if(o.clientValidation === false || this.form.isValid()){
51808             
51809             if (this.form.progressUrl) {
51810                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51811                     (new Date() * 1) + '' + Math.random());
51812                     
51813             } 
51814             
51815             
51816             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51817                 form:this.form.el.dom,
51818                 url:this.getUrl(!isPost),
51819                 method: method,
51820                 params:isPost ? this.getParams() : null,
51821                 isUpload: this.form.fileUpload,
51822                 formData : this.form.formData
51823             }));
51824             
51825             this.uploadProgress();
51826
51827         }else if (o.clientValidation !== false){ // client validation failed
51828             this.failureType = Roo.form.Action.CLIENT_INVALID;
51829             this.form.afterAction(this, false);
51830         }
51831     },
51832
51833     success : function(response)
51834     {
51835         this.uploadComplete= true;
51836         if (this.haveProgress) {
51837             Roo.MessageBox.hide();
51838         }
51839         
51840         
51841         var result = this.processResponse(response);
51842         if(result === true || result.success){
51843             this.form.afterAction(this, true);
51844             return;
51845         }
51846         if(result.errors){
51847             this.form.markInvalid(result.errors);
51848             this.failureType = Roo.form.Action.SERVER_INVALID;
51849         }
51850         this.form.afterAction(this, false);
51851     },
51852     failure : function(response)
51853     {
51854         this.uploadComplete= true;
51855         if (this.haveProgress) {
51856             Roo.MessageBox.hide();
51857         }
51858         
51859         this.response = response;
51860         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51861         this.form.afterAction(this, false);
51862     },
51863     
51864     handleResponse : function(response){
51865         if(this.form.errorReader){
51866             var rs = this.form.errorReader.read(response);
51867             var errors = [];
51868             if(rs.records){
51869                 for(var i = 0, len = rs.records.length; i < len; i++) {
51870                     var r = rs.records[i];
51871                     errors[i] = r.data;
51872                 }
51873             }
51874             if(errors.length < 1){
51875                 errors = null;
51876             }
51877             return {
51878                 success : rs.success,
51879                 errors : errors
51880             };
51881         }
51882         var ret = false;
51883         try {
51884             ret = Roo.decode(response.responseText);
51885         } catch (e) {
51886             ret = {
51887                 success: false,
51888                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51889                 errors : []
51890             };
51891         }
51892         return ret;
51893         
51894     }
51895 });
51896
51897
51898 Roo.form.Action.Load = function(form, options){
51899     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51900     this.reader = this.form.reader;
51901 };
51902
51903 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51904     type : 'load',
51905
51906     run : function(){
51907         
51908         Roo.Ajax.request(Roo.apply(
51909                 this.createCallback(), {
51910                     method:this.getMethod(),
51911                     url:this.getUrl(false),
51912                     params:this.getParams()
51913         }));
51914     },
51915
51916     success : function(response){
51917         
51918         var result = this.processResponse(response);
51919         if(result === true || !result.success || !result.data){
51920             this.failureType = Roo.form.Action.LOAD_FAILURE;
51921             this.form.afterAction(this, false);
51922             return;
51923         }
51924         this.form.clearInvalid();
51925         this.form.setValues(result.data);
51926         this.form.afterAction(this, true);
51927     },
51928
51929     handleResponse : function(response){
51930         if(this.form.reader){
51931             var rs = this.form.reader.read(response);
51932             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51933             return {
51934                 success : rs.success,
51935                 data : data
51936             };
51937         }
51938         return Roo.decode(response.responseText);
51939     }
51940 });
51941
51942 Roo.form.Action.ACTION_TYPES = {
51943     'load' : Roo.form.Action.Load,
51944     'submit' : Roo.form.Action.Submit
51945 };/*
51946  * Based on:
51947  * Ext JS Library 1.1.1
51948  * Copyright(c) 2006-2007, Ext JS, LLC.
51949  *
51950  * Originally Released Under LGPL - original licence link has changed is not relivant.
51951  *
51952  * Fork - LGPL
51953  * <script type="text/javascript">
51954  */
51955  
51956 /**
51957  * @class Roo.form.Layout
51958  * @extends Roo.Component
51959  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51960  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51961  * @constructor
51962  * @param {Object} config Configuration options
51963  */
51964 Roo.form.Layout = function(config){
51965     var xitems = [];
51966     if (config.items) {
51967         xitems = config.items;
51968         delete config.items;
51969     }
51970     Roo.form.Layout.superclass.constructor.call(this, config);
51971     this.stack = [];
51972     Roo.each(xitems, this.addxtype, this);
51973      
51974 };
51975
51976 Roo.extend(Roo.form.Layout, Roo.Component, {
51977     /**
51978      * @cfg {String/Object} autoCreate
51979      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51980      */
51981     /**
51982      * @cfg {String/Object/Function} style
51983      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51984      * a function which returns such a specification.
51985      */
51986     /**
51987      * @cfg {String} labelAlign
51988      * Valid values are "left," "top" and "right" (defaults to "left")
51989      */
51990     /**
51991      * @cfg {Number} labelWidth
51992      * Fixed width in pixels of all field labels (defaults to undefined)
51993      */
51994     /**
51995      * @cfg {Boolean} clear
51996      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51997      */
51998     clear : true,
51999     /**
52000      * @cfg {String} labelSeparator
52001      * The separator to use after field labels (defaults to ':')
52002      */
52003     labelSeparator : ':',
52004     /**
52005      * @cfg {Boolean} hideLabels
52006      * True to suppress the display of field labels in this layout (defaults to false)
52007      */
52008     hideLabels : false,
52009
52010     // private
52011     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52012     
52013     isLayout : true,
52014     
52015     // private
52016     onRender : function(ct, position){
52017         if(this.el){ // from markup
52018             this.el = Roo.get(this.el);
52019         }else {  // generate
52020             var cfg = this.getAutoCreate();
52021             this.el = ct.createChild(cfg, position);
52022         }
52023         if(this.style){
52024             this.el.applyStyles(this.style);
52025         }
52026         if(this.labelAlign){
52027             this.el.addClass('x-form-label-'+this.labelAlign);
52028         }
52029         if(this.hideLabels){
52030             this.labelStyle = "display:none";
52031             this.elementStyle = "padding-left:0;";
52032         }else{
52033             if(typeof this.labelWidth == 'number'){
52034                 this.labelStyle = "width:"+this.labelWidth+"px;";
52035                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52036             }
52037             if(this.labelAlign == 'top'){
52038                 this.labelStyle = "width:auto;";
52039                 this.elementStyle = "padding-left:0;";
52040             }
52041         }
52042         var stack = this.stack;
52043         var slen = stack.length;
52044         if(slen > 0){
52045             if(!this.fieldTpl){
52046                 var t = new Roo.Template(
52047                     '<div class="x-form-item {5}">',
52048                         '<label for="{0}" style="{2}">{1}{4}</label>',
52049                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52050                         '</div>',
52051                     '</div><div class="x-form-clear-left"></div>'
52052                 );
52053                 t.disableFormats = true;
52054                 t.compile();
52055                 Roo.form.Layout.prototype.fieldTpl = t;
52056             }
52057             for(var i = 0; i < slen; i++) {
52058                 if(stack[i].isFormField){
52059                     this.renderField(stack[i]);
52060                 }else{
52061                     this.renderComponent(stack[i]);
52062                 }
52063             }
52064         }
52065         if(this.clear){
52066             this.el.createChild({cls:'x-form-clear'});
52067         }
52068     },
52069
52070     // private
52071     renderField : function(f){
52072         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52073                f.id, //0
52074                f.fieldLabel, //1
52075                f.labelStyle||this.labelStyle||'', //2
52076                this.elementStyle||'', //3
52077                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52078                f.itemCls||this.itemCls||''  //5
52079        ], true).getPrevSibling());
52080     },
52081
52082     // private
52083     renderComponent : function(c){
52084         c.render(c.isLayout ? this.el : this.el.createChild());    
52085     },
52086     /**
52087      * Adds a object form elements (using the xtype property as the factory method.)
52088      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52089      * @param {Object} config 
52090      */
52091     addxtype : function(o)
52092     {
52093         // create the lement.
52094         o.form = this.form;
52095         var fe = Roo.factory(o, Roo.form);
52096         this.form.allItems.push(fe);
52097         this.stack.push(fe);
52098         
52099         if (fe.isFormField) {
52100             this.form.items.add(fe);
52101         }
52102          
52103         return fe;
52104     }
52105 });
52106
52107 /**
52108  * @class Roo.form.Column
52109  * @extends Roo.form.Layout
52110  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52111  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52112  * @constructor
52113  * @param {Object} config Configuration options
52114  */
52115 Roo.form.Column = function(config){
52116     Roo.form.Column.superclass.constructor.call(this, config);
52117 };
52118
52119 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52120     /**
52121      * @cfg {Number/String} width
52122      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52123      */
52124     /**
52125      * @cfg {String/Object} autoCreate
52126      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52127      */
52128
52129     // private
52130     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52131
52132     // private
52133     onRender : function(ct, position){
52134         Roo.form.Column.superclass.onRender.call(this, ct, position);
52135         if(this.width){
52136             this.el.setWidth(this.width);
52137         }
52138     }
52139 });
52140
52141
52142 /**
52143  * @class Roo.form.Row
52144  * @extends Roo.form.Layout
52145  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52146  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52147  * @constructor
52148  * @param {Object} config Configuration options
52149  */
52150
52151  
52152 Roo.form.Row = function(config){
52153     Roo.form.Row.superclass.constructor.call(this, config);
52154 };
52155  
52156 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52157       /**
52158      * @cfg {Number/String} width
52159      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52160      */
52161     /**
52162      * @cfg {Number/String} height
52163      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52164      */
52165     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52166     
52167     padWidth : 20,
52168     // private
52169     onRender : function(ct, position){
52170         //console.log('row render');
52171         if(!this.rowTpl){
52172             var t = new Roo.Template(
52173                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52174                     '<label for="{0}" style="{2}">{1}{4}</label>',
52175                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52176                     '</div>',
52177                 '</div>'
52178             );
52179             t.disableFormats = true;
52180             t.compile();
52181             Roo.form.Layout.prototype.rowTpl = t;
52182         }
52183         this.fieldTpl = this.rowTpl;
52184         
52185         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52186         var labelWidth = 100;
52187         
52188         if ((this.labelAlign != 'top')) {
52189             if (typeof this.labelWidth == 'number') {
52190                 labelWidth = this.labelWidth
52191             }
52192             this.padWidth =  20 + labelWidth;
52193             
52194         }
52195         
52196         Roo.form.Column.superclass.onRender.call(this, ct, position);
52197         if(this.width){
52198             this.el.setWidth(this.width);
52199         }
52200         if(this.height){
52201             this.el.setHeight(this.height);
52202         }
52203     },
52204     
52205     // private
52206     renderField : function(f){
52207         f.fieldEl = this.fieldTpl.append(this.el, [
52208                f.id, f.fieldLabel,
52209                f.labelStyle||this.labelStyle||'',
52210                this.elementStyle||'',
52211                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52212                f.itemCls||this.itemCls||'',
52213                f.width ? f.width + this.padWidth : 160 + this.padWidth
52214        ],true);
52215     }
52216 });
52217  
52218
52219 /**
52220  * @class Roo.form.FieldSet
52221  * @extends Roo.form.Layout
52222  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52223  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52224  * @constructor
52225  * @param {Object} config Configuration options
52226  */
52227 Roo.form.FieldSet = function(config){
52228     Roo.form.FieldSet.superclass.constructor.call(this, config);
52229 };
52230
52231 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52232     /**
52233      * @cfg {String} legend
52234      * The text to display as the legend for the FieldSet (defaults to '')
52235      */
52236     /**
52237      * @cfg {String/Object} autoCreate
52238      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52239      */
52240
52241     // private
52242     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52243
52244     // private
52245     onRender : function(ct, position){
52246         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52247         if(this.legend){
52248             this.setLegend(this.legend);
52249         }
52250     },
52251
52252     // private
52253     setLegend : function(text){
52254         if(this.rendered){
52255             this.el.child('legend').update(text);
52256         }
52257     }
52258 });/*
52259  * Based on:
52260  * Ext JS Library 1.1.1
52261  * Copyright(c) 2006-2007, Ext JS, LLC.
52262  *
52263  * Originally Released Under LGPL - original licence link has changed is not relivant.
52264  *
52265  * Fork - LGPL
52266  * <script type="text/javascript">
52267  */
52268 /**
52269  * @class Roo.form.VTypes
52270  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52271  * @static
52272  */
52273 Roo.form.VTypes = function(){
52274     // closure these in so they are only created once.
52275     var alpha = /^[a-zA-Z_]+$/;
52276     var alphanum = /^[a-zA-Z0-9_]+$/;
52277     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52278     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52279
52280     // All these messages and functions are configurable
52281     return {
52282         /**
52283          * The function used to validate email addresses
52284          * @param {String} value The email address
52285          */
52286         'email' : function(v){
52287             return email.test(v);
52288         },
52289         /**
52290          * The error text to display when the email validation function returns false
52291          * @type String
52292          */
52293         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52294         /**
52295          * The keystroke filter mask to be applied on email input
52296          * @type RegExp
52297          */
52298         'emailMask' : /[a-z0-9_\.\-@]/i,
52299
52300         /**
52301          * The function used to validate URLs
52302          * @param {String} value The URL
52303          */
52304         'url' : function(v){
52305             return url.test(v);
52306         },
52307         /**
52308          * The error text to display when the url validation function returns false
52309          * @type String
52310          */
52311         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52312         
52313         /**
52314          * The function used to validate alpha values
52315          * @param {String} value The value
52316          */
52317         'alpha' : function(v){
52318             return alpha.test(v);
52319         },
52320         /**
52321          * The error text to display when the alpha validation function returns false
52322          * @type String
52323          */
52324         'alphaText' : 'This field should only contain letters and _',
52325         /**
52326          * The keystroke filter mask to be applied on alpha input
52327          * @type RegExp
52328          */
52329         'alphaMask' : /[a-z_]/i,
52330
52331         /**
52332          * The function used to validate alphanumeric values
52333          * @param {String} value The value
52334          */
52335         'alphanum' : function(v){
52336             return alphanum.test(v);
52337         },
52338         /**
52339          * The error text to display when the alphanumeric validation function returns false
52340          * @type String
52341          */
52342         'alphanumText' : 'This field should only contain letters, numbers and _',
52343         /**
52344          * The keystroke filter mask to be applied on alphanumeric input
52345          * @type RegExp
52346          */
52347         'alphanumMask' : /[a-z0-9_]/i
52348     };
52349 }();//<script type="text/javascript">
52350
52351 /**
52352  * @class Roo.form.FCKeditor
52353  * @extends Roo.form.TextArea
52354  * Wrapper around the FCKEditor http://www.fckeditor.net
52355  * @constructor
52356  * Creates a new FCKeditor
52357  * @param {Object} config Configuration options
52358  */
52359 Roo.form.FCKeditor = function(config){
52360     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52361     this.addEvents({
52362          /**
52363          * @event editorinit
52364          * Fired when the editor is initialized - you can add extra handlers here..
52365          * @param {FCKeditor} this
52366          * @param {Object} the FCK object.
52367          */
52368         editorinit : true
52369     });
52370     
52371     
52372 };
52373 Roo.form.FCKeditor.editors = { };
52374 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52375 {
52376     //defaultAutoCreate : {
52377     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52378     //},
52379     // private
52380     /**
52381      * @cfg {Object} fck options - see fck manual for details.
52382      */
52383     fckconfig : false,
52384     
52385     /**
52386      * @cfg {Object} fck toolbar set (Basic or Default)
52387      */
52388     toolbarSet : 'Basic',
52389     /**
52390      * @cfg {Object} fck BasePath
52391      */ 
52392     basePath : '/fckeditor/',
52393     
52394     
52395     frame : false,
52396     
52397     value : '',
52398     
52399    
52400     onRender : function(ct, position)
52401     {
52402         if(!this.el){
52403             this.defaultAutoCreate = {
52404                 tag: "textarea",
52405                 style:"width:300px;height:60px;",
52406                 autocomplete: "new-password"
52407             };
52408         }
52409         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52410         /*
52411         if(this.grow){
52412             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52413             if(this.preventScrollbars){
52414                 this.el.setStyle("overflow", "hidden");
52415             }
52416             this.el.setHeight(this.growMin);
52417         }
52418         */
52419         //console.log('onrender' + this.getId() );
52420         Roo.form.FCKeditor.editors[this.getId()] = this;
52421          
52422
52423         this.replaceTextarea() ;
52424         
52425     },
52426     
52427     getEditor : function() {
52428         return this.fckEditor;
52429     },
52430     /**
52431      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52432      * @param {Mixed} value The value to set
52433      */
52434     
52435     
52436     setValue : function(value)
52437     {
52438         //console.log('setValue: ' + value);
52439         
52440         if(typeof(value) == 'undefined') { // not sure why this is happending...
52441             return;
52442         }
52443         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52444         
52445         //if(!this.el || !this.getEditor()) {
52446         //    this.value = value;
52447             //this.setValue.defer(100,this,[value]);    
52448         //    return;
52449         //} 
52450         
52451         if(!this.getEditor()) {
52452             return;
52453         }
52454         
52455         this.getEditor().SetData(value);
52456         
52457         //
52458
52459     },
52460
52461     /**
52462      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52463      * @return {Mixed} value The field value
52464      */
52465     getValue : function()
52466     {
52467         
52468         if (this.frame && this.frame.dom.style.display == 'none') {
52469             return Roo.form.FCKeditor.superclass.getValue.call(this);
52470         }
52471         
52472         if(!this.el || !this.getEditor()) {
52473            
52474            // this.getValue.defer(100,this); 
52475             return this.value;
52476         }
52477        
52478         
52479         var value=this.getEditor().GetData();
52480         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52481         return Roo.form.FCKeditor.superclass.getValue.call(this);
52482         
52483
52484     },
52485
52486     /**
52487      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52488      * @return {Mixed} value The field value
52489      */
52490     getRawValue : function()
52491     {
52492         if (this.frame && this.frame.dom.style.display == 'none') {
52493             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52494         }
52495         
52496         if(!this.el || !this.getEditor()) {
52497             //this.getRawValue.defer(100,this); 
52498             return this.value;
52499             return;
52500         }
52501         
52502         
52503         
52504         var value=this.getEditor().GetData();
52505         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52506         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52507          
52508     },
52509     
52510     setSize : function(w,h) {
52511         
52512         
52513         
52514         //if (this.frame && this.frame.dom.style.display == 'none') {
52515         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52516         //    return;
52517         //}
52518         //if(!this.el || !this.getEditor()) {
52519         //    this.setSize.defer(100,this, [w,h]); 
52520         //    return;
52521         //}
52522         
52523         
52524         
52525         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52526         
52527         this.frame.dom.setAttribute('width', w);
52528         this.frame.dom.setAttribute('height', h);
52529         this.frame.setSize(w,h);
52530         
52531     },
52532     
52533     toggleSourceEdit : function(value) {
52534         
52535       
52536          
52537         this.el.dom.style.display = value ? '' : 'none';
52538         this.frame.dom.style.display = value ?  'none' : '';
52539         
52540     },
52541     
52542     
52543     focus: function(tag)
52544     {
52545         if (this.frame.dom.style.display == 'none') {
52546             return Roo.form.FCKeditor.superclass.focus.call(this);
52547         }
52548         if(!this.el || !this.getEditor()) {
52549             this.focus.defer(100,this, [tag]); 
52550             return;
52551         }
52552         
52553         
52554         
52555         
52556         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52557         this.getEditor().Focus();
52558         if (tgs.length) {
52559             if (!this.getEditor().Selection.GetSelection()) {
52560                 this.focus.defer(100,this, [tag]); 
52561                 return;
52562             }
52563             
52564             
52565             var r = this.getEditor().EditorDocument.createRange();
52566             r.setStart(tgs[0],0);
52567             r.setEnd(tgs[0],0);
52568             this.getEditor().Selection.GetSelection().removeAllRanges();
52569             this.getEditor().Selection.GetSelection().addRange(r);
52570             this.getEditor().Focus();
52571         }
52572         
52573     },
52574     
52575     
52576     
52577     replaceTextarea : function()
52578     {
52579         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52580             return ;
52581         }
52582         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52583         //{
52584             // We must check the elements firstly using the Id and then the name.
52585         var oTextarea = document.getElementById( this.getId() );
52586         
52587         var colElementsByName = document.getElementsByName( this.getId() ) ;
52588          
52589         oTextarea.style.display = 'none' ;
52590
52591         if ( oTextarea.tabIndex ) {            
52592             this.TabIndex = oTextarea.tabIndex ;
52593         }
52594         
52595         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52596         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52597         this.frame = Roo.get(this.getId() + '___Frame')
52598     },
52599     
52600     _getConfigHtml : function()
52601     {
52602         var sConfig = '' ;
52603
52604         for ( var o in this.fckconfig ) {
52605             sConfig += sConfig.length > 0  ? '&amp;' : '';
52606             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52607         }
52608
52609         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52610     },
52611     
52612     
52613     _getIFrameHtml : function()
52614     {
52615         var sFile = 'fckeditor.html' ;
52616         /* no idea what this is about..
52617         try
52618         {
52619             if ( (/fcksource=true/i).test( window.top.location.search ) )
52620                 sFile = 'fckeditor.original.html' ;
52621         }
52622         catch (e) { 
52623         */
52624
52625         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52626         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52627         
52628         
52629         var html = '<iframe id="' + this.getId() +
52630             '___Frame" src="' + sLink +
52631             '" width="' + this.width +
52632             '" height="' + this.height + '"' +
52633             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52634             ' frameborder="0" scrolling="no"></iframe>' ;
52635
52636         return html ;
52637     },
52638     
52639     _insertHtmlBefore : function( html, element )
52640     {
52641         if ( element.insertAdjacentHTML )       {
52642             // IE
52643             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52644         } else { // Gecko
52645             var oRange = document.createRange() ;
52646             oRange.setStartBefore( element ) ;
52647             var oFragment = oRange.createContextualFragment( html );
52648             element.parentNode.insertBefore( oFragment, element ) ;
52649         }
52650     }
52651     
52652     
52653   
52654     
52655     
52656     
52657     
52658
52659 });
52660
52661 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52662
52663 function FCKeditor_OnComplete(editorInstance){
52664     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52665     f.fckEditor = editorInstance;
52666     //console.log("loaded");
52667     f.fireEvent('editorinit', f, editorInstance);
52668
52669   
52670
52671  
52672
52673
52674
52675
52676
52677
52678
52679
52680
52681
52682
52683
52684
52685
52686
52687 //<script type="text/javascript">
52688 /**
52689  * @class Roo.form.GridField
52690  * @extends Roo.form.Field
52691  * Embed a grid (or editable grid into a form)
52692  * STATUS ALPHA
52693  * 
52694  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52695  * it needs 
52696  * xgrid.store = Roo.data.Store
52697  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52698  * xgrid.store.reader = Roo.data.JsonReader 
52699  * 
52700  * 
52701  * @constructor
52702  * Creates a new GridField
52703  * @param {Object} config Configuration options
52704  */
52705 Roo.form.GridField = function(config){
52706     Roo.form.GridField.superclass.constructor.call(this, config);
52707      
52708 };
52709
52710 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52711     /**
52712      * @cfg {Number} width  - used to restrict width of grid..
52713      */
52714     width : 100,
52715     /**
52716      * @cfg {Number} height - used to restrict height of grid..
52717      */
52718     height : 50,
52719      /**
52720      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52721          * 
52722          *}
52723      */
52724     xgrid : false, 
52725     /**
52726      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52727      * {tag: "input", type: "checkbox", autocomplete: "off"})
52728      */
52729    // defaultAutoCreate : { tag: 'div' },
52730     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52731     /**
52732      * @cfg {String} addTitle Text to include for adding a title.
52733      */
52734     addTitle : false,
52735     //
52736     onResize : function(){
52737         Roo.form.Field.superclass.onResize.apply(this, arguments);
52738     },
52739
52740     initEvents : function(){
52741         // Roo.form.Checkbox.superclass.initEvents.call(this);
52742         // has no events...
52743        
52744     },
52745
52746
52747     getResizeEl : function(){
52748         return this.wrap;
52749     },
52750
52751     getPositionEl : function(){
52752         return this.wrap;
52753     },
52754
52755     // private
52756     onRender : function(ct, position){
52757         
52758         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52759         var style = this.style;
52760         delete this.style;
52761         
52762         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52763         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52764         this.viewEl = this.wrap.createChild({ tag: 'div' });
52765         if (style) {
52766             this.viewEl.applyStyles(style);
52767         }
52768         if (this.width) {
52769             this.viewEl.setWidth(this.width);
52770         }
52771         if (this.height) {
52772             this.viewEl.setHeight(this.height);
52773         }
52774         //if(this.inputValue !== undefined){
52775         //this.setValue(this.value);
52776         
52777         
52778         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52779         
52780         
52781         this.grid.render();
52782         this.grid.getDataSource().on('remove', this.refreshValue, this);
52783         this.grid.getDataSource().on('update', this.refreshValue, this);
52784         this.grid.on('afteredit', this.refreshValue, this);
52785  
52786     },
52787      
52788     
52789     /**
52790      * Sets the value of the item. 
52791      * @param {String} either an object  or a string..
52792      */
52793     setValue : function(v){
52794         //this.value = v;
52795         v = v || []; // empty set..
52796         // this does not seem smart - it really only affects memoryproxy grids..
52797         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52798             var ds = this.grid.getDataSource();
52799             // assumes a json reader..
52800             var data = {}
52801             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52802             ds.loadData( data);
52803         }
52804         // clear selection so it does not get stale.
52805         if (this.grid.sm) { 
52806             this.grid.sm.clearSelections();
52807         }
52808         
52809         Roo.form.GridField.superclass.setValue.call(this, v);
52810         this.refreshValue();
52811         // should load data in the grid really....
52812     },
52813     
52814     // private
52815     refreshValue: function() {
52816          var val = [];
52817         this.grid.getDataSource().each(function(r) {
52818             val.push(r.data);
52819         });
52820         this.el.dom.value = Roo.encode(val);
52821     }
52822     
52823      
52824     
52825     
52826 });/*
52827  * Based on:
52828  * Ext JS Library 1.1.1
52829  * Copyright(c) 2006-2007, Ext JS, LLC.
52830  *
52831  * Originally Released Under LGPL - original licence link has changed is not relivant.
52832  *
52833  * Fork - LGPL
52834  * <script type="text/javascript">
52835  */
52836 /**
52837  * @class Roo.form.DisplayField
52838  * @extends Roo.form.Field
52839  * A generic Field to display non-editable data.
52840  * @cfg {Boolean} closable (true|false) default false
52841  * @constructor
52842  * Creates a new Display Field item.
52843  * @param {Object} config Configuration options
52844  */
52845 Roo.form.DisplayField = function(config){
52846     Roo.form.DisplayField.superclass.constructor.call(this, config);
52847     
52848     this.addEvents({
52849         /**
52850          * @event close
52851          * Fires after the click the close btn
52852              * @param {Roo.form.DisplayField} this
52853              */
52854         close : true
52855     });
52856 };
52857
52858 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52859     inputType:      'hidden',
52860     allowBlank:     true,
52861     readOnly:         true,
52862     
52863  
52864     /**
52865      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52866      */
52867     focusClass : undefined,
52868     /**
52869      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52870      */
52871     fieldClass: 'x-form-field',
52872     
52873      /**
52874      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52875      */
52876     valueRenderer: undefined,
52877     
52878     width: 100,
52879     /**
52880      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52881      * {tag: "input", type: "checkbox", autocomplete: "off"})
52882      */
52883      
52884  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52885  
52886     closable : false,
52887     
52888     onResize : function(){
52889         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52890         
52891     },
52892
52893     initEvents : function(){
52894         // Roo.form.Checkbox.superclass.initEvents.call(this);
52895         // has no events...
52896         
52897         if(this.closable){
52898             this.closeEl.on('click', this.onClose, this);
52899         }
52900        
52901     },
52902
52903
52904     getResizeEl : function(){
52905         return this.wrap;
52906     },
52907
52908     getPositionEl : function(){
52909         return this.wrap;
52910     },
52911
52912     // private
52913     onRender : function(ct, position){
52914         
52915         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52916         //if(this.inputValue !== undefined){
52917         this.wrap = this.el.wrap();
52918         
52919         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52920         
52921         if(this.closable){
52922             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52923         }
52924         
52925         if (this.bodyStyle) {
52926             this.viewEl.applyStyles(this.bodyStyle);
52927         }
52928         //this.viewEl.setStyle('padding', '2px');
52929         
52930         this.setValue(this.value);
52931         
52932     },
52933 /*
52934     // private
52935     initValue : Roo.emptyFn,
52936
52937   */
52938
52939         // private
52940     onClick : function(){
52941         
52942     },
52943
52944     /**
52945      * Sets the checked state of the checkbox.
52946      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52947      */
52948     setValue : function(v){
52949         this.value = v;
52950         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52951         // this might be called before we have a dom element..
52952         if (!this.viewEl) {
52953             return;
52954         }
52955         this.viewEl.dom.innerHTML = html;
52956         Roo.form.DisplayField.superclass.setValue.call(this, v);
52957
52958     },
52959     
52960     onClose : function(e)
52961     {
52962         e.preventDefault();
52963         
52964         this.fireEvent('close', this);
52965     }
52966 });/*
52967  * 
52968  * Licence- LGPL
52969  * 
52970  */
52971
52972 /**
52973  * @class Roo.form.DayPicker
52974  * @extends Roo.form.Field
52975  * A Day picker show [M] [T] [W] ....
52976  * @constructor
52977  * Creates a new Day Picker
52978  * @param {Object} config Configuration options
52979  */
52980 Roo.form.DayPicker= function(config){
52981     Roo.form.DayPicker.superclass.constructor.call(this, config);
52982      
52983 };
52984
52985 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52986     /**
52987      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52988      */
52989     focusClass : undefined,
52990     /**
52991      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52992      */
52993     fieldClass: "x-form-field",
52994    
52995     /**
52996      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52997      * {tag: "input", type: "checkbox", autocomplete: "off"})
52998      */
52999     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
53000     
53001    
53002     actionMode : 'viewEl', 
53003     //
53004     // private
53005  
53006     inputType : 'hidden',
53007     
53008      
53009     inputElement: false, // real input element?
53010     basedOn: false, // ????
53011     
53012     isFormField: true, // not sure where this is needed!!!!
53013
53014     onResize : function(){
53015         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53016         if(!this.boxLabel){
53017             this.el.alignTo(this.wrap, 'c-c');
53018         }
53019     },
53020
53021     initEvents : function(){
53022         Roo.form.Checkbox.superclass.initEvents.call(this);
53023         this.el.on("click", this.onClick,  this);
53024         this.el.on("change", this.onClick,  this);
53025     },
53026
53027
53028     getResizeEl : function(){
53029         return this.wrap;
53030     },
53031
53032     getPositionEl : function(){
53033         return this.wrap;
53034     },
53035
53036     
53037     // private
53038     onRender : function(ct, position){
53039         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53040        
53041         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53042         
53043         var r1 = '<table><tr>';
53044         var r2 = '<tr class="x-form-daypick-icons">';
53045         for (var i=0; i < 7; i++) {
53046             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53047             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53048         }
53049         
53050         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53051         viewEl.select('img').on('click', this.onClick, this);
53052         this.viewEl = viewEl;   
53053         
53054         
53055         // this will not work on Chrome!!!
53056         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53057         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53058         
53059         
53060           
53061
53062     },
53063
53064     // private
53065     initValue : Roo.emptyFn,
53066
53067     /**
53068      * Returns the checked state of the checkbox.
53069      * @return {Boolean} True if checked, else false
53070      */
53071     getValue : function(){
53072         return this.el.dom.value;
53073         
53074     },
53075
53076         // private
53077     onClick : function(e){ 
53078         //this.setChecked(!this.checked);
53079         Roo.get(e.target).toggleClass('x-menu-item-checked');
53080         this.refreshValue();
53081         //if(this.el.dom.checked != this.checked){
53082         //    this.setValue(this.el.dom.checked);
53083        // }
53084     },
53085     
53086     // private
53087     refreshValue : function()
53088     {
53089         var val = '';
53090         this.viewEl.select('img',true).each(function(e,i,n)  {
53091             val += e.is(".x-menu-item-checked") ? String(n) : '';
53092         });
53093         this.setValue(val, true);
53094     },
53095
53096     /**
53097      * Sets the checked state of the checkbox.
53098      * On is always based on a string comparison between inputValue and the param.
53099      * @param {Boolean/String} value - the value to set 
53100      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53101      */
53102     setValue : function(v,suppressEvent){
53103         if (!this.el.dom) {
53104             return;
53105         }
53106         var old = this.el.dom.value ;
53107         this.el.dom.value = v;
53108         if (suppressEvent) {
53109             return ;
53110         }
53111          
53112         // update display..
53113         this.viewEl.select('img',true).each(function(e,i,n)  {
53114             
53115             var on = e.is(".x-menu-item-checked");
53116             var newv = v.indexOf(String(n)) > -1;
53117             if (on != newv) {
53118                 e.toggleClass('x-menu-item-checked');
53119             }
53120             
53121         });
53122         
53123         
53124         this.fireEvent('change', this, v, old);
53125         
53126         
53127     },
53128    
53129     // handle setting of hidden value by some other method!!?!?
53130     setFromHidden: function()
53131     {
53132         if(!this.el){
53133             return;
53134         }
53135         //console.log("SET FROM HIDDEN");
53136         //alert('setFrom hidden');
53137         this.setValue(this.el.dom.value);
53138     },
53139     
53140     onDestroy : function()
53141     {
53142         if(this.viewEl){
53143             Roo.get(this.viewEl).remove();
53144         }
53145          
53146         Roo.form.DayPicker.superclass.onDestroy.call(this);
53147     }
53148
53149 });/*
53150  * RooJS Library 1.1.1
53151  * Copyright(c) 2008-2011  Alan Knowles
53152  *
53153  * License - LGPL
53154  */
53155  
53156
53157 /**
53158  * @class Roo.form.ComboCheck
53159  * @extends Roo.form.ComboBox
53160  * A combobox for multiple select items.
53161  *
53162  * FIXME - could do with a reset button..
53163  * 
53164  * @constructor
53165  * Create a new ComboCheck
53166  * @param {Object} config Configuration options
53167  */
53168 Roo.form.ComboCheck = function(config){
53169     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53170     // should verify some data...
53171     // like
53172     // hiddenName = required..
53173     // displayField = required
53174     // valudField == required
53175     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53176     var _t = this;
53177     Roo.each(req, function(e) {
53178         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53179             throw "Roo.form.ComboCheck : missing value for: " + e;
53180         }
53181     });
53182     
53183     
53184 };
53185
53186 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53187      
53188      
53189     editable : false,
53190      
53191     selectedClass: 'x-menu-item-checked', 
53192     
53193     // private
53194     onRender : function(ct, position){
53195         var _t = this;
53196         
53197         
53198         
53199         if(!this.tpl){
53200             var cls = 'x-combo-list';
53201
53202             
53203             this.tpl =  new Roo.Template({
53204                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53205                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53206                    '<span>{' + this.displayField + '}</span>' +
53207                     '</div>' 
53208                 
53209             });
53210         }
53211  
53212         
53213         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53214         this.view.singleSelect = false;
53215         this.view.multiSelect = true;
53216         this.view.toggleSelect = true;
53217         this.pageTb.add(new Roo.Toolbar.Fill(), {
53218             
53219             text: 'Done',
53220             handler: function()
53221             {
53222                 _t.collapse();
53223             }
53224         });
53225     },
53226     
53227     onViewOver : function(e, t){
53228         // do nothing...
53229         return;
53230         
53231     },
53232     
53233     onViewClick : function(doFocus,index){
53234         return;
53235         
53236     },
53237     select: function () {
53238         //Roo.log("SELECT CALLED");
53239     },
53240      
53241     selectByValue : function(xv, scrollIntoView){
53242         var ar = this.getValueArray();
53243         var sels = [];
53244         
53245         Roo.each(ar, function(v) {
53246             if(v === undefined || v === null){
53247                 return;
53248             }
53249             var r = this.findRecord(this.valueField, v);
53250             if(r){
53251                 sels.push(this.store.indexOf(r))
53252                 
53253             }
53254         },this);
53255         this.view.select(sels);
53256         return false;
53257     },
53258     
53259     
53260     
53261     onSelect : function(record, index){
53262        // Roo.log("onselect Called");
53263        // this is only called by the clear button now..
53264         this.view.clearSelections();
53265         this.setValue('[]');
53266         if (this.value != this.valueBefore) {
53267             this.fireEvent('change', this, this.value, this.valueBefore);
53268             this.valueBefore = this.value;
53269         }
53270     },
53271     getValueArray : function()
53272     {
53273         var ar = [] ;
53274         
53275         try {
53276             //Roo.log(this.value);
53277             if (typeof(this.value) == 'undefined') {
53278                 return [];
53279             }
53280             var ar = Roo.decode(this.value);
53281             return  ar instanceof Array ? ar : []; //?? valid?
53282             
53283         } catch(e) {
53284             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53285             return [];
53286         }
53287          
53288     },
53289     expand : function ()
53290     {
53291         
53292         Roo.form.ComboCheck.superclass.expand.call(this);
53293         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53294         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53295         
53296
53297     },
53298     
53299     collapse : function(){
53300         Roo.form.ComboCheck.superclass.collapse.call(this);
53301         var sl = this.view.getSelectedIndexes();
53302         var st = this.store;
53303         var nv = [];
53304         var tv = [];
53305         var r;
53306         Roo.each(sl, function(i) {
53307             r = st.getAt(i);
53308             nv.push(r.get(this.valueField));
53309         },this);
53310         this.setValue(Roo.encode(nv));
53311         if (this.value != this.valueBefore) {
53312
53313             this.fireEvent('change', this, this.value, this.valueBefore);
53314             this.valueBefore = this.value;
53315         }
53316         
53317     },
53318     
53319     setValue : function(v){
53320         // Roo.log(v);
53321         this.value = v;
53322         
53323         var vals = this.getValueArray();
53324         var tv = [];
53325         Roo.each(vals, function(k) {
53326             var r = this.findRecord(this.valueField, k);
53327             if(r){
53328                 tv.push(r.data[this.displayField]);
53329             }else if(this.valueNotFoundText !== undefined){
53330                 tv.push( this.valueNotFoundText );
53331             }
53332         },this);
53333        // Roo.log(tv);
53334         
53335         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53336         this.hiddenField.value = v;
53337         this.value = v;
53338     }
53339     
53340 });/*
53341  * Based on:
53342  * Ext JS Library 1.1.1
53343  * Copyright(c) 2006-2007, Ext JS, LLC.
53344  *
53345  * Originally Released Under LGPL - original licence link has changed is not relivant.
53346  *
53347  * Fork - LGPL
53348  * <script type="text/javascript">
53349  */
53350  
53351 /**
53352  * @class Roo.form.Signature
53353  * @extends Roo.form.Field
53354  * Signature field.  
53355  * @constructor
53356  * 
53357  * @param {Object} config Configuration options
53358  */
53359
53360 Roo.form.Signature = function(config){
53361     Roo.form.Signature.superclass.constructor.call(this, config);
53362     
53363     this.addEvents({// not in used??
53364          /**
53365          * @event confirm
53366          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53367              * @param {Roo.form.Signature} combo This combo box
53368              */
53369         'confirm' : true,
53370         /**
53371          * @event reset
53372          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53373              * @param {Roo.form.ComboBox} combo This combo box
53374              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53375              */
53376         'reset' : true
53377     });
53378 };
53379
53380 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53381     /**
53382      * @cfg {Object} labels Label to use when rendering a form.
53383      * defaults to 
53384      * labels : { 
53385      *      clear : "Clear",
53386      *      confirm : "Confirm"
53387      *  }
53388      */
53389     labels : { 
53390         clear : "Clear",
53391         confirm : "Confirm"
53392     },
53393     /**
53394      * @cfg {Number} width The signature panel width (defaults to 300)
53395      */
53396     width: 300,
53397     /**
53398      * @cfg {Number} height The signature panel height (defaults to 100)
53399      */
53400     height : 100,
53401     /**
53402      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53403      */
53404     allowBlank : false,
53405     
53406     //private
53407     // {Object} signPanel The signature SVG panel element (defaults to {})
53408     signPanel : {},
53409     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53410     isMouseDown : false,
53411     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53412     isConfirmed : false,
53413     // {String} signatureTmp SVG mapping string (defaults to empty string)
53414     signatureTmp : '',
53415     
53416     
53417     defaultAutoCreate : { // modified by initCompnoent..
53418         tag: "input",
53419         type:"hidden"
53420     },
53421
53422     // private
53423     onRender : function(ct, position){
53424         
53425         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53426         
53427         this.wrap = this.el.wrap({
53428             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53429         });
53430         
53431         this.createToolbar(this);
53432         this.signPanel = this.wrap.createChild({
53433                 tag: 'div',
53434                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53435             }, this.el
53436         );
53437             
53438         this.svgID = Roo.id();
53439         this.svgEl = this.signPanel.createChild({
53440               xmlns : 'http://www.w3.org/2000/svg',
53441               tag : 'svg',
53442               id : this.svgID + "-svg",
53443               width: this.width,
53444               height: this.height,
53445               viewBox: '0 0 '+this.width+' '+this.height,
53446               cn : [
53447                 {
53448                     tag: "rect",
53449                     id: this.svgID + "-svg-r",
53450                     width: this.width,
53451                     height: this.height,
53452                     fill: "#ffa"
53453                 },
53454                 {
53455                     tag: "line",
53456                     id: this.svgID + "-svg-l",
53457                     x1: "0", // start
53458                     y1: (this.height*0.8), // start set the line in 80% of height
53459                     x2: this.width, // end
53460                     y2: (this.height*0.8), // end set the line in 80% of height
53461                     'stroke': "#666",
53462                     'stroke-width': "1",
53463                     'stroke-dasharray': "3",
53464                     'shape-rendering': "crispEdges",
53465                     'pointer-events': "none"
53466                 },
53467                 {
53468                     tag: "path",
53469                     id: this.svgID + "-svg-p",
53470                     'stroke': "navy",
53471                     'stroke-width': "3",
53472                     'fill': "none",
53473                     'pointer-events': 'none'
53474                 }
53475               ]
53476         });
53477         this.createSVG();
53478         this.svgBox = this.svgEl.dom.getScreenCTM();
53479     },
53480     createSVG : function(){ 
53481         var svg = this.signPanel;
53482         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53483         var t = this;
53484
53485         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53486         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53487         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53488         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53489         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53490         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53491         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53492         
53493     },
53494     isTouchEvent : function(e){
53495         return e.type.match(/^touch/);
53496     },
53497     getCoords : function (e) {
53498         var pt    = this.svgEl.dom.createSVGPoint();
53499         pt.x = e.clientX; 
53500         pt.y = e.clientY;
53501         if (this.isTouchEvent(e)) {
53502             pt.x =  e.targetTouches[0].clientX;
53503             pt.y = e.targetTouches[0].clientY;
53504         }
53505         var a = this.svgEl.dom.getScreenCTM();
53506         var b = a.inverse();
53507         var mx = pt.matrixTransform(b);
53508         return mx.x + ',' + mx.y;
53509     },
53510     //mouse event headler 
53511     down : function (e) {
53512         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53513         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53514         
53515         this.isMouseDown = true;
53516         
53517         e.preventDefault();
53518     },
53519     move : function (e) {
53520         if (this.isMouseDown) {
53521             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53522             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53523         }
53524         
53525         e.preventDefault();
53526     },
53527     up : function (e) {
53528         this.isMouseDown = false;
53529         var sp = this.signatureTmp.split(' ');
53530         
53531         if(sp.length > 1){
53532             if(!sp[sp.length-2].match(/^L/)){
53533                 sp.pop();
53534                 sp.pop();
53535                 sp.push("");
53536                 this.signatureTmp = sp.join(" ");
53537             }
53538         }
53539         if(this.getValue() != this.signatureTmp){
53540             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53541             this.isConfirmed = false;
53542         }
53543         e.preventDefault();
53544     },
53545     
53546     /**
53547      * Protected method that will not generally be called directly. It
53548      * is called when the editor creates its toolbar. Override this method if you need to
53549      * add custom toolbar buttons.
53550      * @param {HtmlEditor} editor
53551      */
53552     createToolbar : function(editor){
53553          function btn(id, toggle, handler){
53554             var xid = fid + '-'+ id ;
53555             return {
53556                 id : xid,
53557                 cmd : id,
53558                 cls : 'x-btn-icon x-edit-'+id,
53559                 enableToggle:toggle !== false,
53560                 scope: editor, // was editor...
53561                 handler:handler||editor.relayBtnCmd,
53562                 clickEvent:'mousedown',
53563                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53564                 tabIndex:-1
53565             };
53566         }
53567         
53568         
53569         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53570         this.tb = tb;
53571         this.tb.add(
53572            {
53573                 cls : ' x-signature-btn x-signature-'+id,
53574                 scope: editor, // was editor...
53575                 handler: this.reset,
53576                 clickEvent:'mousedown',
53577                 text: this.labels.clear
53578             },
53579             {
53580                  xtype : 'Fill',
53581                  xns: Roo.Toolbar
53582             }, 
53583             {
53584                 cls : '  x-signature-btn x-signature-'+id,
53585                 scope: editor, // was editor...
53586                 handler: this.confirmHandler,
53587                 clickEvent:'mousedown',
53588                 text: this.labels.confirm
53589             }
53590         );
53591     
53592     },
53593     //public
53594     /**
53595      * when user is clicked confirm then show this image.....
53596      * 
53597      * @return {String} Image Data URI
53598      */
53599     getImageDataURI : function(){
53600         var svg = this.svgEl.dom.parentNode.innerHTML;
53601         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53602         return src; 
53603     },
53604     /**
53605      * 
53606      * @return {Boolean} this.isConfirmed
53607      */
53608     getConfirmed : function(){
53609         return this.isConfirmed;
53610     },
53611     /**
53612      * 
53613      * @return {Number} this.width
53614      */
53615     getWidth : function(){
53616         return this.width;
53617     },
53618     /**
53619      * 
53620      * @return {Number} this.height
53621      */
53622     getHeight : function(){
53623         return this.height;
53624     },
53625     // private
53626     getSignature : function(){
53627         return this.signatureTmp;
53628     },
53629     // private
53630     reset : function(){
53631         this.signatureTmp = '';
53632         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53633         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53634         this.isConfirmed = false;
53635         Roo.form.Signature.superclass.reset.call(this);
53636     },
53637     setSignature : function(s){
53638         this.signatureTmp = s;
53639         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53640         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53641         this.setValue(s);
53642         this.isConfirmed = false;
53643         Roo.form.Signature.superclass.reset.call(this);
53644     }, 
53645     test : function(){
53646 //        Roo.log(this.signPanel.dom.contentWindow.up())
53647     },
53648     //private
53649     setConfirmed : function(){
53650         
53651         
53652         
53653 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53654     },
53655     // private
53656     confirmHandler : function(){
53657         if(!this.getSignature()){
53658             return;
53659         }
53660         
53661         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53662         this.setValue(this.getSignature());
53663         this.isConfirmed = true;
53664         
53665         this.fireEvent('confirm', this);
53666     },
53667     // private
53668     // Subclasses should provide the validation implementation by overriding this
53669     validateValue : function(value){
53670         if(this.allowBlank){
53671             return true;
53672         }
53673         
53674         if(this.isConfirmed){
53675             return true;
53676         }
53677         return false;
53678     }
53679 });/*
53680  * Based on:
53681  * Ext JS Library 1.1.1
53682  * Copyright(c) 2006-2007, Ext JS, LLC.
53683  *
53684  * Originally Released Under LGPL - original licence link has changed is not relivant.
53685  *
53686  * Fork - LGPL
53687  * <script type="text/javascript">
53688  */
53689  
53690
53691 /**
53692  * @class Roo.form.ComboBox
53693  * @extends Roo.form.TriggerField
53694  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53695  * @constructor
53696  * Create a new ComboBox.
53697  * @param {Object} config Configuration options
53698  */
53699 Roo.form.Select = function(config){
53700     Roo.form.Select.superclass.constructor.call(this, config);
53701      
53702 };
53703
53704 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53705     /**
53706      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53707      */
53708     /**
53709      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53710      * rendering into an Roo.Editor, defaults to false)
53711      */
53712     /**
53713      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53714      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53715      */
53716     /**
53717      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53718      */
53719     /**
53720      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53721      * the dropdown list (defaults to undefined, with no header element)
53722      */
53723
53724      /**
53725      * @cfg {String/Roo.Template} tpl The template to use to render the output
53726      */
53727      
53728     // private
53729     defaultAutoCreate : {tag: "select"  },
53730     /**
53731      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53732      */
53733     listWidth: undefined,
53734     /**
53735      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53736      * mode = 'remote' or 'text' if mode = 'local')
53737      */
53738     displayField: undefined,
53739     /**
53740      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53741      * mode = 'remote' or 'value' if mode = 'local'). 
53742      * Note: use of a valueField requires the user make a selection
53743      * in order for a value to be mapped.
53744      */
53745     valueField: undefined,
53746     
53747     
53748     /**
53749      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53750      * field's data value (defaults to the underlying DOM element's name)
53751      */
53752     hiddenName: undefined,
53753     /**
53754      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53755      */
53756     listClass: '',
53757     /**
53758      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53759      */
53760     selectedClass: 'x-combo-selected',
53761     /**
53762      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53763      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53764      * which displays a downward arrow icon).
53765      */
53766     triggerClass : 'x-form-arrow-trigger',
53767     /**
53768      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53769      */
53770     shadow:'sides',
53771     /**
53772      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53773      * anchor positions (defaults to 'tl-bl')
53774      */
53775     listAlign: 'tl-bl?',
53776     /**
53777      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53778      */
53779     maxHeight: 300,
53780     /**
53781      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53782      * query specified by the allQuery config option (defaults to 'query')
53783      */
53784     triggerAction: 'query',
53785     /**
53786      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53787      * (defaults to 4, does not apply if editable = false)
53788      */
53789     minChars : 4,
53790     /**
53791      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53792      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53793      */
53794     typeAhead: false,
53795     /**
53796      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53797      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53798      */
53799     queryDelay: 500,
53800     /**
53801      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53802      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53803      */
53804     pageSize: 0,
53805     /**
53806      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53807      * when editable = true (defaults to false)
53808      */
53809     selectOnFocus:false,
53810     /**
53811      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53812      */
53813     queryParam: 'query',
53814     /**
53815      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53816      * when mode = 'remote' (defaults to 'Loading...')
53817      */
53818     loadingText: 'Loading...',
53819     /**
53820      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53821      */
53822     resizable: false,
53823     /**
53824      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53825      */
53826     handleHeight : 8,
53827     /**
53828      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53829      * traditional select (defaults to true)
53830      */
53831     editable: true,
53832     /**
53833      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53834      */
53835     allQuery: '',
53836     /**
53837      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53838      */
53839     mode: 'remote',
53840     /**
53841      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53842      * listWidth has a higher value)
53843      */
53844     minListWidth : 70,
53845     /**
53846      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53847      * allow the user to set arbitrary text into the field (defaults to false)
53848      */
53849     forceSelection:false,
53850     /**
53851      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53852      * if typeAhead = true (defaults to 250)
53853      */
53854     typeAheadDelay : 250,
53855     /**
53856      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53857      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53858      */
53859     valueNotFoundText : undefined,
53860     
53861     /**
53862      * @cfg {String} defaultValue The value displayed after loading the store.
53863      */
53864     defaultValue: '',
53865     
53866     /**
53867      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53868      */
53869     blockFocus : false,
53870     
53871     /**
53872      * @cfg {Boolean} disableClear Disable showing of clear button.
53873      */
53874     disableClear : false,
53875     /**
53876      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53877      */
53878     alwaysQuery : false,
53879     
53880     //private
53881     addicon : false,
53882     editicon: false,
53883     
53884     // element that contains real text value.. (when hidden is used..)
53885      
53886     // private
53887     onRender : function(ct, position){
53888         Roo.form.Field.prototype.onRender.call(this, ct, position);
53889         
53890         if(this.store){
53891             this.store.on('beforeload', this.onBeforeLoad, this);
53892             this.store.on('load', this.onLoad, this);
53893             this.store.on('loadexception', this.onLoadException, this);
53894             this.store.load({});
53895         }
53896         
53897         
53898         
53899     },
53900
53901     // private
53902     initEvents : function(){
53903         //Roo.form.ComboBox.superclass.initEvents.call(this);
53904  
53905     },
53906
53907     onDestroy : function(){
53908        
53909         if(this.store){
53910             this.store.un('beforeload', this.onBeforeLoad, this);
53911             this.store.un('load', this.onLoad, this);
53912             this.store.un('loadexception', this.onLoadException, this);
53913         }
53914         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53915     },
53916
53917     // private
53918     fireKey : function(e){
53919         if(e.isNavKeyPress() && !this.list.isVisible()){
53920             this.fireEvent("specialkey", this, e);
53921         }
53922     },
53923
53924     // private
53925     onResize: function(w, h){
53926         
53927         return; 
53928     
53929         
53930     },
53931
53932     /**
53933      * Allow or prevent the user from directly editing the field text.  If false is passed,
53934      * the user will only be able to select from the items defined in the dropdown list.  This method
53935      * is the runtime equivalent of setting the 'editable' config option at config time.
53936      * @param {Boolean} value True to allow the user to directly edit the field text
53937      */
53938     setEditable : function(value){
53939          
53940     },
53941
53942     // private
53943     onBeforeLoad : function(){
53944         
53945         Roo.log("Select before load");
53946         return;
53947     
53948         this.innerList.update(this.loadingText ?
53949                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53950         //this.restrictHeight();
53951         this.selectedIndex = -1;
53952     },
53953
53954     // private
53955     onLoad : function(){
53956
53957     
53958         var dom = this.el.dom;
53959         dom.innerHTML = '';
53960          var od = dom.ownerDocument;
53961          
53962         if (this.emptyText) {
53963             var op = od.createElement('option');
53964             op.setAttribute('value', '');
53965             op.innerHTML = String.format('{0}', this.emptyText);
53966             dom.appendChild(op);
53967         }
53968         if(this.store.getCount() > 0){
53969            
53970             var vf = this.valueField;
53971             var df = this.displayField;
53972             this.store.data.each(function(r) {
53973                 // which colmsn to use... testing - cdoe / title..
53974                 var op = od.createElement('option');
53975                 op.setAttribute('value', r.data[vf]);
53976                 op.innerHTML = String.format('{0}', r.data[df]);
53977                 dom.appendChild(op);
53978             });
53979             if (typeof(this.defaultValue != 'undefined')) {
53980                 this.setValue(this.defaultValue);
53981             }
53982             
53983              
53984         }else{
53985             //this.onEmptyResults();
53986         }
53987         //this.el.focus();
53988     },
53989     // private
53990     onLoadException : function()
53991     {
53992         dom.innerHTML = '';
53993             
53994         Roo.log("Select on load exception");
53995         return;
53996     
53997         this.collapse();
53998         Roo.log(this.store.reader.jsonData);
53999         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54000             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54001         }
54002         
54003         
54004     },
54005     // private
54006     onTypeAhead : function(){
54007          
54008     },
54009
54010     // private
54011     onSelect : function(record, index){
54012         Roo.log('on select?');
54013         return;
54014         if(this.fireEvent('beforeselect', this, record, index) !== false){
54015             this.setFromData(index > -1 ? record.data : false);
54016             this.collapse();
54017             this.fireEvent('select', this, record, index);
54018         }
54019     },
54020
54021     /**
54022      * Returns the currently selected field value or empty string if no value is set.
54023      * @return {String} value The selected value
54024      */
54025     getValue : function(){
54026         var dom = this.el.dom;
54027         this.value = dom.options[dom.selectedIndex].value;
54028         return this.value;
54029         
54030     },
54031
54032     /**
54033      * Clears any text/value currently set in the field
54034      */
54035     clearValue : function(){
54036         this.value = '';
54037         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54038         
54039     },
54040
54041     /**
54042      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54043      * will be displayed in the field.  If the value does not match the data value of an existing item,
54044      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54045      * Otherwise the field will be blank (although the value will still be set).
54046      * @param {String} value The value to match
54047      */
54048     setValue : function(v){
54049         var d = this.el.dom;
54050         for (var i =0; i < d.options.length;i++) {
54051             if (v == d.options[i].value) {
54052                 d.selectedIndex = i;
54053                 this.value = v;
54054                 return;
54055             }
54056         }
54057         this.clearValue();
54058     },
54059     /**
54060      * @property {Object} the last set data for the element
54061      */
54062     
54063     lastData : false,
54064     /**
54065      * Sets the value of the field based on a object which is related to the record format for the store.
54066      * @param {Object} value the value to set as. or false on reset?
54067      */
54068     setFromData : function(o){
54069         Roo.log('setfrom data?');
54070          
54071         
54072         
54073     },
54074     // private
54075     reset : function(){
54076         this.clearValue();
54077     },
54078     // private
54079     findRecord : function(prop, value){
54080         
54081         return false;
54082     
54083         var record;
54084         if(this.store.getCount() > 0){
54085             this.store.each(function(r){
54086                 if(r.data[prop] == value){
54087                     record = r;
54088                     return false;
54089                 }
54090                 return true;
54091             });
54092         }
54093         return record;
54094     },
54095     
54096     getName: function()
54097     {
54098         // returns hidden if it's set..
54099         if (!this.rendered) {return ''};
54100         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54101         
54102     },
54103      
54104
54105     
54106
54107     // private
54108     onEmptyResults : function(){
54109         Roo.log('empty results');
54110         //this.collapse();
54111     },
54112
54113     /**
54114      * Returns true if the dropdown list is expanded, else false.
54115      */
54116     isExpanded : function(){
54117         return false;
54118     },
54119
54120     /**
54121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54123      * @param {String} value The data value of the item to select
54124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54125      * selected item if it is not currently in view (defaults to true)
54126      * @return {Boolean} True if the value matched an item in the list, else false
54127      */
54128     selectByValue : function(v, scrollIntoView){
54129         Roo.log('select By Value');
54130         return false;
54131     
54132         if(v !== undefined && v !== null){
54133             var r = this.findRecord(this.valueField || this.displayField, v);
54134             if(r){
54135                 this.select(this.store.indexOf(r), scrollIntoView);
54136                 return true;
54137             }
54138         }
54139         return false;
54140     },
54141
54142     /**
54143      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54144      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54145      * @param {Number} index The zero-based index of the list item to select
54146      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54147      * selected item if it is not currently in view (defaults to true)
54148      */
54149     select : function(index, scrollIntoView){
54150         Roo.log('select ');
54151         return  ;
54152         
54153         this.selectedIndex = index;
54154         this.view.select(index);
54155         if(scrollIntoView !== false){
54156             var el = this.view.getNode(index);
54157             if(el){
54158                 this.innerList.scrollChildIntoView(el, false);
54159             }
54160         }
54161     },
54162
54163       
54164
54165     // private
54166     validateBlur : function(){
54167         
54168         return;
54169         
54170     },
54171
54172     // private
54173     initQuery : function(){
54174         this.doQuery(this.getRawValue());
54175     },
54176
54177     // private
54178     doForce : function(){
54179         if(this.el.dom.value.length > 0){
54180             this.el.dom.value =
54181                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54182              
54183         }
54184     },
54185
54186     /**
54187      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54188      * query allowing the query action to be canceled if needed.
54189      * @param {String} query The SQL query to execute
54190      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54191      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54192      * saved in the current store (defaults to false)
54193      */
54194     doQuery : function(q, forceAll){
54195         
54196         Roo.log('doQuery?');
54197         if(q === undefined || q === null){
54198             q = '';
54199         }
54200         var qe = {
54201             query: q,
54202             forceAll: forceAll,
54203             combo: this,
54204             cancel:false
54205         };
54206         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54207             return false;
54208         }
54209         q = qe.query;
54210         forceAll = qe.forceAll;
54211         if(forceAll === true || (q.length >= this.minChars)){
54212             if(this.lastQuery != q || this.alwaysQuery){
54213                 this.lastQuery = q;
54214                 if(this.mode == 'local'){
54215                     this.selectedIndex = -1;
54216                     if(forceAll){
54217                         this.store.clearFilter();
54218                     }else{
54219                         this.store.filter(this.displayField, q);
54220                     }
54221                     this.onLoad();
54222                 }else{
54223                     this.store.baseParams[this.queryParam] = q;
54224                     this.store.load({
54225                         params: this.getParams(q)
54226                     });
54227                     this.expand();
54228                 }
54229             }else{
54230                 this.selectedIndex = -1;
54231                 this.onLoad();   
54232             }
54233         }
54234     },
54235
54236     // private
54237     getParams : function(q){
54238         var p = {};
54239         //p[this.queryParam] = q;
54240         if(this.pageSize){
54241             p.start = 0;
54242             p.limit = this.pageSize;
54243         }
54244         return p;
54245     },
54246
54247     /**
54248      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54249      */
54250     collapse : function(){
54251         
54252     },
54253
54254     // private
54255     collapseIf : function(e){
54256         
54257     },
54258
54259     /**
54260      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54261      */
54262     expand : function(){
54263         
54264     } ,
54265
54266     // private
54267      
54268
54269     /** 
54270     * @cfg {Boolean} grow 
54271     * @hide 
54272     */
54273     /** 
54274     * @cfg {Number} growMin 
54275     * @hide 
54276     */
54277     /** 
54278     * @cfg {Number} growMax 
54279     * @hide 
54280     */
54281     /**
54282      * @hide
54283      * @method autoSize
54284      */
54285     
54286     setWidth : function()
54287     {
54288         
54289     },
54290     getResizeEl : function(){
54291         return this.el;
54292     }
54293 });//<script type="text/javasscript">
54294  
54295
54296 /**
54297  * @class Roo.DDView
54298  * A DnD enabled version of Roo.View.
54299  * @param {Element/String} container The Element in which to create the View.
54300  * @param {String} tpl The template string used to create the markup for each element of the View
54301  * @param {Object} config The configuration properties. These include all the config options of
54302  * {@link Roo.View} plus some specific to this class.<br>
54303  * <p>
54304  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54305  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54306  * <p>
54307  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54308 .x-view-drag-insert-above {
54309         border-top:1px dotted #3366cc;
54310 }
54311 .x-view-drag-insert-below {
54312         border-bottom:1px dotted #3366cc;
54313 }
54314 </code></pre>
54315  * 
54316  */
54317  
54318 Roo.DDView = function(container, tpl, config) {
54319     Roo.DDView.superclass.constructor.apply(this, arguments);
54320     this.getEl().setStyle("outline", "0px none");
54321     this.getEl().unselectable();
54322     if (this.dragGroup) {
54323         this.setDraggable(this.dragGroup.split(","));
54324     }
54325     if (this.dropGroup) {
54326         this.setDroppable(this.dropGroup.split(","));
54327     }
54328     if (this.deletable) {
54329         this.setDeletable();
54330     }
54331     this.isDirtyFlag = false;
54332         this.addEvents({
54333                 "drop" : true
54334         });
54335 };
54336
54337 Roo.extend(Roo.DDView, Roo.View, {
54338 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54339 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54340 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54341 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54342
54343         isFormField: true,
54344
54345         reset: Roo.emptyFn,
54346         
54347         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54348
54349         validate: function() {
54350                 return true;
54351         },
54352         
54353         destroy: function() {
54354                 this.purgeListeners();
54355                 this.getEl.removeAllListeners();
54356                 this.getEl().remove();
54357                 if (this.dragZone) {
54358                         if (this.dragZone.destroy) {
54359                                 this.dragZone.destroy();
54360                         }
54361                 }
54362                 if (this.dropZone) {
54363                         if (this.dropZone.destroy) {
54364                                 this.dropZone.destroy();
54365                         }
54366                 }
54367         },
54368
54369 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54370         getName: function() {
54371                 return this.name;
54372         },
54373
54374 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54375         setValue: function(v) {
54376                 if (!this.store) {
54377                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54378                 }
54379                 var data = {};
54380                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54381                 this.store.proxy = new Roo.data.MemoryProxy(data);
54382                 this.store.load();
54383         },
54384
54385 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54386         getValue: function() {
54387                 var result = '(';
54388                 this.store.each(function(rec) {
54389                         result += rec.id + ',';
54390                 });
54391                 return result.substr(0, result.length - 1) + ')';
54392         },
54393         
54394         getIds: function() {
54395                 var i = 0, result = new Array(this.store.getCount());
54396                 this.store.each(function(rec) {
54397                         result[i++] = rec.id;
54398                 });
54399                 return result;
54400         },
54401         
54402         isDirty: function() {
54403                 return this.isDirtyFlag;
54404         },
54405
54406 /**
54407  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54408  *      whole Element becomes the target, and this causes the drop gesture to append.
54409  */
54410     getTargetFromEvent : function(e) {
54411                 var target = e.getTarget();
54412                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54413                 target = target.parentNode;
54414                 }
54415                 if (!target) {
54416                         target = this.el.dom.lastChild || this.el.dom;
54417                 }
54418                 return target;
54419     },
54420
54421 /**
54422  *      Create the drag data which consists of an object which has the property "ddel" as
54423  *      the drag proxy element. 
54424  */
54425     getDragData : function(e) {
54426         var target = this.findItemFromChild(e.getTarget());
54427                 if(target) {
54428                         this.handleSelection(e);
54429                         var selNodes = this.getSelectedNodes();
54430             var dragData = {
54431                 source: this,
54432                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54433                 nodes: selNodes,
54434                 records: []
54435                         };
54436                         var selectedIndices = this.getSelectedIndexes();
54437                         for (var i = 0; i < selectedIndices.length; i++) {
54438                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54439                         }
54440                         if (selNodes.length == 1) {
54441                                 dragData.ddel = target.cloneNode(true); // the div element
54442                         } else {
54443                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54444                                 div.className = 'multi-proxy';
54445                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54446                                         div.appendChild(selNodes[i].cloneNode(true));
54447                                 }
54448                                 dragData.ddel = div;
54449                         }
54450             //console.log(dragData)
54451             //console.log(dragData.ddel.innerHTML)
54452                         return dragData;
54453                 }
54454         //console.log('nodragData')
54455                 return false;
54456     },
54457     
54458 /**     Specify to which ddGroup items in this DDView may be dragged. */
54459     setDraggable: function(ddGroup) {
54460         if (ddGroup instanceof Array) {
54461                 Roo.each(ddGroup, this.setDraggable, this);
54462                 return;
54463         }
54464         if (this.dragZone) {
54465                 this.dragZone.addToGroup(ddGroup);
54466         } else {
54467                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54468                                 containerScroll: true,
54469                                 ddGroup: ddGroup 
54470
54471                         });
54472 //                      Draggability implies selection. DragZone's mousedown selects the element.
54473                         if (!this.multiSelect) { this.singleSelect = true; }
54474
54475 //                      Wire the DragZone's handlers up to methods in *this*
54476                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54477                 }
54478     },
54479
54480 /**     Specify from which ddGroup this DDView accepts drops. */
54481     setDroppable: function(ddGroup) {
54482         if (ddGroup instanceof Array) {
54483                 Roo.each(ddGroup, this.setDroppable, this);
54484                 return;
54485         }
54486         if (this.dropZone) {
54487                 this.dropZone.addToGroup(ddGroup);
54488         } else {
54489                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54490                                 containerScroll: true,
54491                                 ddGroup: ddGroup
54492                         });
54493
54494 //                      Wire the DropZone's handlers up to methods in *this*
54495                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54496                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54497                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54498                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54499                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54500                 }
54501     },
54502
54503 /**     Decide whether to drop above or below a View node. */
54504     getDropPoint : function(e, n, dd){
54505         if (n == this.el.dom) { return "above"; }
54506                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54507                 var c = t + (b - t) / 2;
54508                 var y = Roo.lib.Event.getPageY(e);
54509                 if(y <= c) {
54510                         return "above";
54511                 }else{
54512                         return "below";
54513                 }
54514     },
54515
54516     onNodeEnter : function(n, dd, e, data){
54517                 return false;
54518     },
54519     
54520     onNodeOver : function(n, dd, e, data){
54521                 var pt = this.getDropPoint(e, n, dd);
54522                 // set the insert point style on the target node
54523                 var dragElClass = this.dropNotAllowed;
54524                 if (pt) {
54525                         var targetElClass;
54526                         if (pt == "above"){
54527                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54528                                 targetElClass = "x-view-drag-insert-above";
54529                         } else {
54530                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54531                                 targetElClass = "x-view-drag-insert-below";
54532                         }
54533                         if (this.lastInsertClass != targetElClass){
54534                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54535                                 this.lastInsertClass = targetElClass;
54536                         }
54537                 }
54538                 return dragElClass;
54539         },
54540
54541     onNodeOut : function(n, dd, e, data){
54542                 this.removeDropIndicators(n);
54543     },
54544
54545     onNodeDrop : function(n, dd, e, data){
54546         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54547                 return false;
54548         }
54549         var pt = this.getDropPoint(e, n, dd);
54550                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54551                 if (pt == "below") { insertAt++; }
54552                 for (var i = 0; i < data.records.length; i++) {
54553                         var r = data.records[i];
54554                         var dup = this.store.getById(r.id);
54555                         if (dup && (dd != this.dragZone)) {
54556                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54557                         } else {
54558                                 if (data.copy) {
54559                                         this.store.insert(insertAt++, r.copy());
54560                                 } else {
54561                                         data.source.isDirtyFlag = true;
54562                                         r.store.remove(r);
54563                                         this.store.insert(insertAt++, r);
54564                                 }
54565                                 this.isDirtyFlag = true;
54566                         }
54567                 }
54568                 this.dragZone.cachedTarget = null;
54569                 return true;
54570     },
54571
54572     removeDropIndicators : function(n){
54573                 if(n){
54574                         Roo.fly(n).removeClass([
54575                                 "x-view-drag-insert-above",
54576                                 "x-view-drag-insert-below"]);
54577                         this.lastInsertClass = "_noclass";
54578                 }
54579     },
54580
54581 /**
54582  *      Utility method. Add a delete option to the DDView's context menu.
54583  *      @param {String} imageUrl The URL of the "delete" icon image.
54584  */
54585         setDeletable: function(imageUrl) {
54586                 if (!this.singleSelect && !this.multiSelect) {
54587                         this.singleSelect = true;
54588                 }
54589                 var c = this.getContextMenu();
54590                 this.contextMenu.on("itemclick", function(item) {
54591                         switch (item.id) {
54592                                 case "delete":
54593                                         this.remove(this.getSelectedIndexes());
54594                                         break;
54595                         }
54596                 }, this);
54597                 this.contextMenu.add({
54598                         icon: imageUrl,
54599                         id: "delete",
54600                         text: 'Delete'
54601                 });
54602         },
54603         
54604 /**     Return the context menu for this DDView. */
54605         getContextMenu: function() {
54606                 if (!this.contextMenu) {
54607 //                      Create the View's context menu
54608                         this.contextMenu = new Roo.menu.Menu({
54609                                 id: this.id + "-contextmenu"
54610                         });
54611                         this.el.on("contextmenu", this.showContextMenu, this);
54612                 }
54613                 return this.contextMenu;
54614         },
54615         
54616         disableContextMenu: function() {
54617                 if (this.contextMenu) {
54618                         this.el.un("contextmenu", this.showContextMenu, this);
54619                 }
54620         },
54621
54622         showContextMenu: function(e, item) {
54623         item = this.findItemFromChild(e.getTarget());
54624                 if (item) {
54625                         e.stopEvent();
54626                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54627                         this.contextMenu.showAt(e.getXY());
54628             }
54629     },
54630
54631 /**
54632  *      Remove {@link Roo.data.Record}s at the specified indices.
54633  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54634  */
54635     remove: function(selectedIndices) {
54636                 selectedIndices = [].concat(selectedIndices);
54637                 for (var i = 0; i < selectedIndices.length; i++) {
54638                         var rec = this.store.getAt(selectedIndices[i]);
54639                         this.store.remove(rec);
54640                 }
54641     },
54642
54643 /**
54644  *      Double click fires the event, but also, if this is draggable, and there is only one other
54645  *      related DropZone, it transfers the selected node.
54646  */
54647     onDblClick : function(e){
54648         var item = this.findItemFromChild(e.getTarget());
54649         if(item){
54650             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54651                 return false;
54652             }
54653             if (this.dragGroup) {
54654                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54655                     while (targets.indexOf(this.dropZone) > -1) {
54656                             targets.remove(this.dropZone);
54657                                 }
54658                     if (targets.length == 1) {
54659                                         this.dragZone.cachedTarget = null;
54660                         var el = Roo.get(targets[0].getEl());
54661                         var box = el.getBox(true);
54662                         targets[0].onNodeDrop(el.dom, {
54663                                 target: el.dom,
54664                                 xy: [box.x, box.y + box.height - 1]
54665                         }, null, this.getDragData(e));
54666                     }
54667                 }
54668         }
54669     },
54670     
54671     handleSelection: function(e) {
54672                 this.dragZone.cachedTarget = null;
54673         var item = this.findItemFromChild(e.getTarget());
54674         if (!item) {
54675                 this.clearSelections(true);
54676                 return;
54677         }
54678                 if (item && (this.multiSelect || this.singleSelect)){
54679                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54680                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54681                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54682                                 this.unselect(item);
54683                         } else {
54684                                 this.select(item, this.multiSelect && e.ctrlKey);
54685                                 this.lastSelection = item;
54686                         }
54687                 }
54688     },
54689
54690     onItemClick : function(item, index, e){
54691                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54692                         return false;
54693                 }
54694                 return true;
54695     },
54696
54697     unselect : function(nodeInfo, suppressEvent){
54698                 var node = this.getNode(nodeInfo);
54699                 if(node && this.isSelected(node)){
54700                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54701                                 Roo.fly(node).removeClass(this.selectedClass);
54702                                 this.selections.remove(node);
54703                                 if(!suppressEvent){
54704                                         this.fireEvent("selectionchange", this, this.selections);
54705                                 }
54706                         }
54707                 }
54708     }
54709 });
54710 /*
54711  * Based on:
54712  * Ext JS Library 1.1.1
54713  * Copyright(c) 2006-2007, Ext JS, LLC.
54714  *
54715  * Originally Released Under LGPL - original licence link has changed is not relivant.
54716  *
54717  * Fork - LGPL
54718  * <script type="text/javascript">
54719  */
54720  
54721 /**
54722  * @class Roo.LayoutManager
54723  * @extends Roo.util.Observable
54724  * Base class for layout managers.
54725  */
54726 Roo.LayoutManager = function(container, config){
54727     Roo.LayoutManager.superclass.constructor.call(this);
54728     this.el = Roo.get(container);
54729     // ie scrollbar fix
54730     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54731         document.body.scroll = "no";
54732     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54733         this.el.position('relative');
54734     }
54735     this.id = this.el.id;
54736     this.el.addClass("x-layout-container");
54737     /** false to disable window resize monitoring @type Boolean */
54738     this.monitorWindowResize = true;
54739     this.regions = {};
54740     this.addEvents({
54741         /**
54742          * @event layout
54743          * Fires when a layout is performed. 
54744          * @param {Roo.LayoutManager} this
54745          */
54746         "layout" : true,
54747         /**
54748          * @event regionresized
54749          * Fires when the user resizes a region. 
54750          * @param {Roo.LayoutRegion} region The resized region
54751          * @param {Number} newSize The new size (width for east/west, height for north/south)
54752          */
54753         "regionresized" : true,
54754         /**
54755          * @event regioncollapsed
54756          * Fires when a region is collapsed. 
54757          * @param {Roo.LayoutRegion} region The collapsed region
54758          */
54759         "regioncollapsed" : true,
54760         /**
54761          * @event regionexpanded
54762          * Fires when a region is expanded.  
54763          * @param {Roo.LayoutRegion} region The expanded region
54764          */
54765         "regionexpanded" : true
54766     });
54767     this.updating = false;
54768     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54769 };
54770
54771 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54772     /**
54773      * Returns true if this layout is currently being updated
54774      * @return {Boolean}
54775      */
54776     isUpdating : function(){
54777         return this.updating; 
54778     },
54779     
54780     /**
54781      * Suspend the LayoutManager from doing auto-layouts while
54782      * making multiple add or remove calls
54783      */
54784     beginUpdate : function(){
54785         this.updating = true;    
54786     },
54787     
54788     /**
54789      * Restore auto-layouts and optionally disable the manager from performing a layout
54790      * @param {Boolean} noLayout true to disable a layout update 
54791      */
54792     endUpdate : function(noLayout){
54793         this.updating = false;
54794         if(!noLayout){
54795             this.layout();
54796         }    
54797     },
54798     
54799     layout: function(){
54800         
54801     },
54802     
54803     onRegionResized : function(region, newSize){
54804         this.fireEvent("regionresized", region, newSize);
54805         this.layout();
54806     },
54807     
54808     onRegionCollapsed : function(region){
54809         this.fireEvent("regioncollapsed", region);
54810     },
54811     
54812     onRegionExpanded : function(region){
54813         this.fireEvent("regionexpanded", region);
54814     },
54815         
54816     /**
54817      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54818      * performs box-model adjustments.
54819      * @return {Object} The size as an object {width: (the width), height: (the height)}
54820      */
54821     getViewSize : function(){
54822         var size;
54823         if(this.el.dom != document.body){
54824             size = this.el.getSize();
54825         }else{
54826             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54827         }
54828         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54829         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54830         return size;
54831     },
54832     
54833     /**
54834      * Returns the Element this layout is bound to.
54835      * @return {Roo.Element}
54836      */
54837     getEl : function(){
54838         return this.el;
54839     },
54840     
54841     /**
54842      * Returns the specified region.
54843      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54844      * @return {Roo.LayoutRegion}
54845      */
54846     getRegion : function(target){
54847         return this.regions[target.toLowerCase()];
54848     },
54849     
54850     onWindowResize : function(){
54851         if(this.monitorWindowResize){
54852             this.layout();
54853         }
54854     }
54855 });/*
54856  * Based on:
54857  * Ext JS Library 1.1.1
54858  * Copyright(c) 2006-2007, Ext JS, LLC.
54859  *
54860  * Originally Released Under LGPL - original licence link has changed is not relivant.
54861  *
54862  * Fork - LGPL
54863  * <script type="text/javascript">
54864  */
54865 /**
54866  * @class Roo.BorderLayout
54867  * @extends Roo.LayoutManager
54868  * @children Roo.ContentPanel
54869  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54870  * please see: <br><br>
54871  * <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>
54872  * <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>
54873  * Example:
54874  <pre><code>
54875  var layout = new Roo.BorderLayout(document.body, {
54876     north: {
54877         initialSize: 25,
54878         titlebar: false
54879     },
54880     west: {
54881         split:true,
54882         initialSize: 200,
54883         minSize: 175,
54884         maxSize: 400,
54885         titlebar: true,
54886         collapsible: true
54887     },
54888     east: {
54889         split:true,
54890         initialSize: 202,
54891         minSize: 175,
54892         maxSize: 400,
54893         titlebar: true,
54894         collapsible: true
54895     },
54896     south: {
54897         split:true,
54898         initialSize: 100,
54899         minSize: 100,
54900         maxSize: 200,
54901         titlebar: true,
54902         collapsible: true
54903     },
54904     center: {
54905         titlebar: true,
54906         autoScroll:true,
54907         resizeTabs: true,
54908         minTabWidth: 50,
54909         preferredTabWidth: 150
54910     }
54911 });
54912
54913 // shorthand
54914 var CP = Roo.ContentPanel;
54915
54916 layout.beginUpdate();
54917 layout.add("north", new CP("north", "North"));
54918 layout.add("south", new CP("south", {title: "South", closable: true}));
54919 layout.add("west", new CP("west", {title: "West"}));
54920 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54921 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54922 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54923 layout.getRegion("center").showPanel("center1");
54924 layout.endUpdate();
54925 </code></pre>
54926
54927 <b>The container the layout is rendered into can be either the body element or any other element.
54928 If it is not the body element, the container needs to either be an absolute positioned element,
54929 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54930 the container size if it is not the body element.</b>
54931
54932 * @constructor
54933 * Create a new BorderLayout
54934 * @param {String/HTMLElement/Element} container The container this layout is bound to
54935 * @param {Object} config Configuration options
54936  */
54937 Roo.BorderLayout = function(container, config){
54938     config = config || {};
54939     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54940     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54941     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54942         var target = this.factory.validRegions[i];
54943         if(config[target]){
54944             this.addRegion(target, config[target]);
54945         }
54946     }
54947 };
54948
54949 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54950         
54951         /**
54952          * @cfg {Roo.LayoutRegion} east
54953          */
54954         /**
54955          * @cfg {Roo.LayoutRegion} west
54956          */
54957         /**
54958          * @cfg {Roo.LayoutRegion} north
54959          */
54960         /**
54961          * @cfg {Roo.LayoutRegion} south
54962          */
54963         /**
54964          * @cfg {Roo.LayoutRegion} center
54965          */
54966     /**
54967      * Creates and adds a new region if it doesn't already exist.
54968      * @param {String} target The target region key (north, south, east, west or center).
54969      * @param {Object} config The regions config object
54970      * @return {BorderLayoutRegion} The new region
54971      */
54972     addRegion : function(target, config){
54973         if(!this.regions[target]){
54974             var r = this.factory.create(target, this, config);
54975             this.bindRegion(target, r);
54976         }
54977         return this.regions[target];
54978     },
54979
54980     // private (kinda)
54981     bindRegion : function(name, r){
54982         this.regions[name] = r;
54983         r.on("visibilitychange", this.layout, this);
54984         r.on("paneladded", this.layout, this);
54985         r.on("panelremoved", this.layout, this);
54986         r.on("invalidated", this.layout, this);
54987         r.on("resized", this.onRegionResized, this);
54988         r.on("collapsed", this.onRegionCollapsed, this);
54989         r.on("expanded", this.onRegionExpanded, this);
54990     },
54991
54992     /**
54993      * Performs a layout update.
54994      */
54995     layout : function(){
54996         if(this.updating) {
54997             return;
54998         }
54999         var size = this.getViewSize();
55000         var w = size.width;
55001         var h = size.height;
55002         var centerW = w;
55003         var centerH = h;
55004         var centerY = 0;
55005         var centerX = 0;
55006         //var x = 0, y = 0;
55007
55008         var rs = this.regions;
55009         var north = rs["north"];
55010         var south = rs["south"]; 
55011         var west = rs["west"];
55012         var east = rs["east"];
55013         var center = rs["center"];
55014         //if(this.hideOnLayout){ // not supported anymore
55015             //c.el.setStyle("display", "none");
55016         //}
55017         if(north && north.isVisible()){
55018             var b = north.getBox();
55019             var m = north.getMargins();
55020             b.width = w - (m.left+m.right);
55021             b.x = m.left;
55022             b.y = m.top;
55023             centerY = b.height + b.y + m.bottom;
55024             centerH -= centerY;
55025             north.updateBox(this.safeBox(b));
55026         }
55027         if(south && south.isVisible()){
55028             var b = south.getBox();
55029             var m = south.getMargins();
55030             b.width = w - (m.left+m.right);
55031             b.x = m.left;
55032             var totalHeight = (b.height + m.top + m.bottom);
55033             b.y = h - totalHeight + m.top;
55034             centerH -= totalHeight;
55035             south.updateBox(this.safeBox(b));
55036         }
55037         if(west && west.isVisible()){
55038             var b = west.getBox();
55039             var m = west.getMargins();
55040             b.height = centerH - (m.top+m.bottom);
55041             b.x = m.left;
55042             b.y = centerY + m.top;
55043             var totalWidth = (b.width + m.left + m.right);
55044             centerX += totalWidth;
55045             centerW -= totalWidth;
55046             west.updateBox(this.safeBox(b));
55047         }
55048         if(east && east.isVisible()){
55049             var b = east.getBox();
55050             var m = east.getMargins();
55051             b.height = centerH - (m.top+m.bottom);
55052             var totalWidth = (b.width + m.left + m.right);
55053             b.x = w - totalWidth + m.left;
55054             b.y = centerY + m.top;
55055             centerW -= totalWidth;
55056             east.updateBox(this.safeBox(b));
55057         }
55058         if(center){
55059             var m = center.getMargins();
55060             var centerBox = {
55061                 x: centerX + m.left,
55062                 y: centerY + m.top,
55063                 width: centerW - (m.left+m.right),
55064                 height: centerH - (m.top+m.bottom)
55065             };
55066             //if(this.hideOnLayout){
55067                 //center.el.setStyle("display", "block");
55068             //}
55069             center.updateBox(this.safeBox(centerBox));
55070         }
55071         this.el.repaint();
55072         this.fireEvent("layout", this);
55073     },
55074
55075     // private
55076     safeBox : function(box){
55077         box.width = Math.max(0, box.width);
55078         box.height = Math.max(0, box.height);
55079         return box;
55080     },
55081
55082     /**
55083      * Adds a ContentPanel (or subclass) to this layout.
55084      * @param {String} target The target region key (north, south, east, west or center).
55085      * @param {Roo.ContentPanel} panel The panel to add
55086      * @return {Roo.ContentPanel} The added panel
55087      */
55088     add : function(target, panel){
55089          
55090         target = target.toLowerCase();
55091         return this.regions[target].add(panel);
55092     },
55093
55094     /**
55095      * Remove a ContentPanel (or subclass) to this layout.
55096      * @param {String} target The target region key (north, south, east, west or center).
55097      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55098      * @return {Roo.ContentPanel} The removed panel
55099      */
55100     remove : function(target, panel){
55101         target = target.toLowerCase();
55102         return this.regions[target].remove(panel);
55103     },
55104
55105     /**
55106      * Searches all regions for a panel with the specified id
55107      * @param {String} panelId
55108      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55109      */
55110     findPanel : function(panelId){
55111         var rs = this.regions;
55112         for(var target in rs){
55113             if(typeof rs[target] != "function"){
55114                 var p = rs[target].getPanel(panelId);
55115                 if(p){
55116                     return p;
55117                 }
55118             }
55119         }
55120         return null;
55121     },
55122
55123     /**
55124      * Searches all regions for a panel with the specified id and activates (shows) it.
55125      * @param {String/ContentPanel} panelId The panels id or the panel itself
55126      * @return {Roo.ContentPanel} The shown panel or null
55127      */
55128     showPanel : function(panelId) {
55129       var rs = this.regions;
55130       for(var target in rs){
55131          var r = rs[target];
55132          if(typeof r != "function"){
55133             if(r.hasPanel(panelId)){
55134                return r.showPanel(panelId);
55135             }
55136          }
55137       }
55138       return null;
55139    },
55140
55141    /**
55142      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55143      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55144      */
55145     restoreState : function(provider){
55146         if(!provider){
55147             provider = Roo.state.Manager;
55148         }
55149         var sm = new Roo.LayoutStateManager();
55150         sm.init(this, provider);
55151     },
55152
55153     /**
55154      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55155      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55156      * a valid ContentPanel config object.  Example:
55157      * <pre><code>
55158 // Create the main layout
55159 var layout = new Roo.BorderLayout('main-ct', {
55160     west: {
55161         split:true,
55162         minSize: 175,
55163         titlebar: true
55164     },
55165     center: {
55166         title:'Components'
55167     }
55168 }, 'main-ct');
55169
55170 // Create and add multiple ContentPanels at once via configs
55171 layout.batchAdd({
55172    west: {
55173        id: 'source-files',
55174        autoCreate:true,
55175        title:'Ext Source Files',
55176        autoScroll:true,
55177        fitToFrame:true
55178    },
55179    center : {
55180        el: cview,
55181        autoScroll:true,
55182        fitToFrame:true,
55183        toolbar: tb,
55184        resizeEl:'cbody'
55185    }
55186 });
55187 </code></pre>
55188      * @param {Object} regions An object containing ContentPanel configs by region name
55189      */
55190     batchAdd : function(regions){
55191         this.beginUpdate();
55192         for(var rname in regions){
55193             var lr = this.regions[rname];
55194             if(lr){
55195                 this.addTypedPanels(lr, regions[rname]);
55196             }
55197         }
55198         this.endUpdate();
55199     },
55200
55201     // private
55202     addTypedPanels : function(lr, ps){
55203         if(typeof ps == 'string'){
55204             lr.add(new Roo.ContentPanel(ps));
55205         }
55206         else if(ps instanceof Array){
55207             for(var i =0, len = ps.length; i < len; i++){
55208                 this.addTypedPanels(lr, ps[i]);
55209             }
55210         }
55211         else if(!ps.events){ // raw config?
55212             var el = ps.el;
55213             delete ps.el; // prevent conflict
55214             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55215         }
55216         else {  // panel object assumed!
55217             lr.add(ps);
55218         }
55219     },
55220     /**
55221      * Adds a xtype elements to the layout.
55222      * <pre><code>
55223
55224 layout.addxtype({
55225        xtype : 'ContentPanel',
55226        region: 'west',
55227        items: [ .... ]
55228    }
55229 );
55230
55231 layout.addxtype({
55232         xtype : 'NestedLayoutPanel',
55233         region: 'west',
55234         layout: {
55235            center: { },
55236            west: { }   
55237         },
55238         items : [ ... list of content panels or nested layout panels.. ]
55239    }
55240 );
55241 </code></pre>
55242      * @param {Object} cfg Xtype definition of item to add.
55243      */
55244     addxtype : function(cfg)
55245     {
55246         // basically accepts a pannel...
55247         // can accept a layout region..!?!?
55248         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55249         
55250         if (!cfg.xtype.match(/Panel$/)) {
55251             return false;
55252         }
55253         var ret = false;
55254         
55255         if (typeof(cfg.region) == 'undefined') {
55256             Roo.log("Failed to add Panel, region was not set");
55257             Roo.log(cfg);
55258             return false;
55259         }
55260         var region = cfg.region;
55261         delete cfg.region;
55262         
55263           
55264         var xitems = [];
55265         if (cfg.items) {
55266             xitems = cfg.items;
55267             delete cfg.items;
55268         }
55269         var nb = false;
55270         
55271         switch(cfg.xtype) 
55272         {
55273             case 'ContentPanel':  // ContentPanel (el, cfg)
55274             case 'ScrollPanel':  // ContentPanel (el, cfg)
55275             case 'ViewPanel': 
55276                 if(cfg.autoCreate) {
55277                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55278                 } else {
55279                     var el = this.el.createChild();
55280                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55281                 }
55282                 
55283                 this.add(region, ret);
55284                 break;
55285             
55286             
55287             case 'TreePanel': // our new panel!
55288                 cfg.el = this.el.createChild();
55289                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55290                 this.add(region, ret);
55291                 break;
55292             
55293             case 'NestedLayoutPanel': 
55294                 // create a new Layout (which is  a Border Layout...
55295                 var el = this.el.createChild();
55296                 var clayout = cfg.layout;
55297                 delete cfg.layout;
55298                 clayout.items   = clayout.items  || [];
55299                 // replace this exitems with the clayout ones..
55300                 xitems = clayout.items;
55301                  
55302                 
55303                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55304                     cfg.background = false;
55305                 }
55306                 var layout = new Roo.BorderLayout(el, clayout);
55307                 
55308                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55309                 //console.log('adding nested layout panel '  + cfg.toSource());
55310                 this.add(region, ret);
55311                 nb = {}; /// find first...
55312                 break;
55313                 
55314             case 'GridPanel': 
55315             
55316                 // needs grid and region
55317                 
55318                 //var el = this.getRegion(region).el.createChild();
55319                 var el = this.el.createChild();
55320                 // create the grid first...
55321                 
55322                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55323                 delete cfg.grid;
55324                 if (region == 'center' && this.active ) {
55325                     cfg.background = false;
55326                 }
55327                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55328                 
55329                 this.add(region, ret);
55330                 if (cfg.background) {
55331                     ret.on('activate', function(gp) {
55332                         if (!gp.grid.rendered) {
55333                             gp.grid.render();
55334                         }
55335                     });
55336                 } else {
55337                     grid.render();
55338                 }
55339                 break;
55340            
55341            
55342            
55343                 
55344                 
55345                 
55346             default:
55347                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55348                     
55349                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55350                     this.add(region, ret);
55351                 } else {
55352                 
55353                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55354                     return null;
55355                 }
55356                 
55357              // GridPanel (grid, cfg)
55358             
55359         }
55360         this.beginUpdate();
55361         // add children..
55362         var region = '';
55363         var abn = {};
55364         Roo.each(xitems, function(i)  {
55365             region = nb && i.region ? i.region : false;
55366             
55367             var add = ret.addxtype(i);
55368            
55369             if (region) {
55370                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55371                 if (!i.background) {
55372                     abn[region] = nb[region] ;
55373                 }
55374             }
55375             
55376         });
55377         this.endUpdate();
55378
55379         // make the last non-background panel active..
55380         //if (nb) { Roo.log(abn); }
55381         if (nb) {
55382             
55383             for(var r in abn) {
55384                 region = this.getRegion(r);
55385                 if (region) {
55386                     // tried using nb[r], but it does not work..
55387                      
55388                     region.showPanel(abn[r]);
55389                    
55390                 }
55391             }
55392         }
55393         return ret;
55394         
55395     }
55396 });
55397
55398 /**
55399  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55400  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55401  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55402  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55403  * <pre><code>
55404 // shorthand
55405 var CP = Roo.ContentPanel;
55406
55407 var layout = Roo.BorderLayout.create({
55408     north: {
55409         initialSize: 25,
55410         titlebar: false,
55411         panels: [new CP("north", "North")]
55412     },
55413     west: {
55414         split:true,
55415         initialSize: 200,
55416         minSize: 175,
55417         maxSize: 400,
55418         titlebar: true,
55419         collapsible: true,
55420         panels: [new CP("west", {title: "West"})]
55421     },
55422     east: {
55423         split:true,
55424         initialSize: 202,
55425         minSize: 175,
55426         maxSize: 400,
55427         titlebar: true,
55428         collapsible: true,
55429         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55430     },
55431     south: {
55432         split:true,
55433         initialSize: 100,
55434         minSize: 100,
55435         maxSize: 200,
55436         titlebar: true,
55437         collapsible: true,
55438         panels: [new CP("south", {title: "South", closable: true})]
55439     },
55440     center: {
55441         titlebar: true,
55442         autoScroll:true,
55443         resizeTabs: true,
55444         minTabWidth: 50,
55445         preferredTabWidth: 150,
55446         panels: [
55447             new CP("center1", {title: "Close Me", closable: true}),
55448             new CP("center2", {title: "Center Panel", closable: false})
55449         ]
55450     }
55451 }, document.body);
55452
55453 layout.getRegion("center").showPanel("center1");
55454 </code></pre>
55455  * @param config
55456  * @param targetEl
55457  */
55458 Roo.BorderLayout.create = function(config, targetEl){
55459     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55460     layout.beginUpdate();
55461     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55462     for(var j = 0, jlen = regions.length; j < jlen; j++){
55463         var lr = regions[j];
55464         if(layout.regions[lr] && config[lr].panels){
55465             var r = layout.regions[lr];
55466             var ps = config[lr].panels;
55467             layout.addTypedPanels(r, ps);
55468         }
55469     }
55470     layout.endUpdate();
55471     return layout;
55472 };
55473
55474 // private
55475 Roo.BorderLayout.RegionFactory = {
55476     // private
55477     validRegions : ["north","south","east","west","center"],
55478
55479     // private
55480     create : function(target, mgr, config){
55481         target = target.toLowerCase();
55482         if(config.lightweight || config.basic){
55483             return new Roo.BasicLayoutRegion(mgr, config, target);
55484         }
55485         switch(target){
55486             case "north":
55487                 return new Roo.NorthLayoutRegion(mgr, config);
55488             case "south":
55489                 return new Roo.SouthLayoutRegion(mgr, config);
55490             case "east":
55491                 return new Roo.EastLayoutRegion(mgr, config);
55492             case "west":
55493                 return new Roo.WestLayoutRegion(mgr, config);
55494             case "center":
55495                 return new Roo.CenterLayoutRegion(mgr, config);
55496         }
55497         throw 'Layout region "'+target+'" not supported.';
55498     }
55499 };/*
55500  * Based on:
55501  * Ext JS Library 1.1.1
55502  * Copyright(c) 2006-2007, Ext JS, LLC.
55503  *
55504  * Originally Released Under LGPL - original licence link has changed is not relivant.
55505  *
55506  * Fork - LGPL
55507  * <script type="text/javascript">
55508  */
55509  
55510 /**
55511  * @class Roo.BasicLayoutRegion
55512  * @extends Roo.util.Observable
55513  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55514  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55515  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55516  */
55517 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55518     this.mgr = mgr;
55519     this.position  = pos;
55520     this.events = {
55521         /**
55522          * @scope Roo.BasicLayoutRegion
55523          */
55524         
55525         /**
55526          * @event beforeremove
55527          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55528          * @param {Roo.LayoutRegion} this
55529          * @param {Roo.ContentPanel} panel The panel
55530          * @param {Object} e The cancel event object
55531          */
55532         "beforeremove" : true,
55533         /**
55534          * @event invalidated
55535          * Fires when the layout for this region is changed.
55536          * @param {Roo.LayoutRegion} this
55537          */
55538         "invalidated" : true,
55539         /**
55540          * @event visibilitychange
55541          * Fires when this region is shown or hidden 
55542          * @param {Roo.LayoutRegion} this
55543          * @param {Boolean} visibility true or false
55544          */
55545         "visibilitychange" : true,
55546         /**
55547          * @event paneladded
55548          * Fires when a panel is added. 
55549          * @param {Roo.LayoutRegion} this
55550          * @param {Roo.ContentPanel} panel The panel
55551          */
55552         "paneladded" : true,
55553         /**
55554          * @event panelremoved
55555          * Fires when a panel is removed. 
55556          * @param {Roo.LayoutRegion} this
55557          * @param {Roo.ContentPanel} panel The panel
55558          */
55559         "panelremoved" : true,
55560         /**
55561          * @event beforecollapse
55562          * Fires when this region before collapse.
55563          * @param {Roo.LayoutRegion} this
55564          */
55565         "beforecollapse" : true,
55566         /**
55567          * @event collapsed
55568          * Fires when this region is collapsed.
55569          * @param {Roo.LayoutRegion} this
55570          */
55571         "collapsed" : true,
55572         /**
55573          * @event expanded
55574          * Fires when this region is expanded.
55575          * @param {Roo.LayoutRegion} this
55576          */
55577         "expanded" : true,
55578         /**
55579          * @event slideshow
55580          * Fires when this region is slid into view.
55581          * @param {Roo.LayoutRegion} this
55582          */
55583         "slideshow" : true,
55584         /**
55585          * @event slidehide
55586          * Fires when this region slides out of view. 
55587          * @param {Roo.LayoutRegion} this
55588          */
55589         "slidehide" : true,
55590         /**
55591          * @event panelactivated
55592          * Fires when a panel is activated. 
55593          * @param {Roo.LayoutRegion} this
55594          * @param {Roo.ContentPanel} panel The activated panel
55595          */
55596         "panelactivated" : true,
55597         /**
55598          * @event resized
55599          * Fires when the user resizes this region. 
55600          * @param {Roo.LayoutRegion} this
55601          * @param {Number} newSize The new size (width for east/west, height for north/south)
55602          */
55603         "resized" : true
55604     };
55605     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55606     this.panels = new Roo.util.MixedCollection();
55607     this.panels.getKey = this.getPanelId.createDelegate(this);
55608     this.box = null;
55609     this.activePanel = null;
55610     // ensure listeners are added...
55611     
55612     if (config.listeners || config.events) {
55613         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55614             listeners : config.listeners || {},
55615             events : config.events || {}
55616         });
55617     }
55618     
55619     if(skipConfig !== true){
55620         this.applyConfig(config);
55621     }
55622 };
55623
55624 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55625     getPanelId : function(p){
55626         return p.getId();
55627     },
55628     
55629     applyConfig : function(config){
55630         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55631         this.config = config;
55632         
55633     },
55634     
55635     /**
55636      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55637      * the width, for horizontal (north, south) the height.
55638      * @param {Number} newSize The new width or height
55639      */
55640     resizeTo : function(newSize){
55641         var el = this.el ? this.el :
55642                  (this.activePanel ? this.activePanel.getEl() : null);
55643         if(el){
55644             switch(this.position){
55645                 case "east":
55646                 case "west":
55647                     el.setWidth(newSize);
55648                     this.fireEvent("resized", this, newSize);
55649                 break;
55650                 case "north":
55651                 case "south":
55652                     el.setHeight(newSize);
55653                     this.fireEvent("resized", this, newSize);
55654                 break;                
55655             }
55656         }
55657     },
55658     
55659     getBox : function(){
55660         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55661     },
55662     
55663     getMargins : function(){
55664         return this.margins;
55665     },
55666     
55667     updateBox : function(box){
55668         this.box = box;
55669         var el = this.activePanel.getEl();
55670         el.dom.style.left = box.x + "px";
55671         el.dom.style.top = box.y + "px";
55672         this.activePanel.setSize(box.width, box.height);
55673     },
55674     
55675     /**
55676      * Returns the container element for this region.
55677      * @return {Roo.Element}
55678      */
55679     getEl : function(){
55680         return this.activePanel;
55681     },
55682     
55683     /**
55684      * Returns true if this region is currently visible.
55685      * @return {Boolean}
55686      */
55687     isVisible : function(){
55688         return this.activePanel ? true : false;
55689     },
55690     
55691     setActivePanel : function(panel){
55692         panel = this.getPanel(panel);
55693         if(this.activePanel && this.activePanel != panel){
55694             this.activePanel.setActiveState(false);
55695             this.activePanel.getEl().setLeftTop(-10000,-10000);
55696         }
55697         this.activePanel = panel;
55698         panel.setActiveState(true);
55699         if(this.box){
55700             panel.setSize(this.box.width, this.box.height);
55701         }
55702         this.fireEvent("panelactivated", this, panel);
55703         this.fireEvent("invalidated");
55704     },
55705     
55706     /**
55707      * Show the specified panel.
55708      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55709      * @return {Roo.ContentPanel} The shown panel or null
55710      */
55711     showPanel : function(panel){
55712         if(panel = this.getPanel(panel)){
55713             this.setActivePanel(panel);
55714         }
55715         return panel;
55716     },
55717     
55718     /**
55719      * Get the active panel for this region.
55720      * @return {Roo.ContentPanel} The active panel or null
55721      */
55722     getActivePanel : function(){
55723         return this.activePanel;
55724     },
55725     
55726     /**
55727      * Add the passed ContentPanel(s)
55728      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55729      * @return {Roo.ContentPanel} The panel added (if only one was added)
55730      */
55731     add : function(panel){
55732         if(arguments.length > 1){
55733             for(var i = 0, len = arguments.length; i < len; i++) {
55734                 this.add(arguments[i]);
55735             }
55736             return null;
55737         }
55738         if(this.hasPanel(panel)){
55739             this.showPanel(panel);
55740             return panel;
55741         }
55742         var el = panel.getEl();
55743         if(el.dom.parentNode != this.mgr.el.dom){
55744             this.mgr.el.dom.appendChild(el.dom);
55745         }
55746         if(panel.setRegion){
55747             panel.setRegion(this);
55748         }
55749         this.panels.add(panel);
55750         el.setStyle("position", "absolute");
55751         if(!panel.background){
55752             this.setActivePanel(panel);
55753             if(this.config.initialSize && this.panels.getCount()==1){
55754                 this.resizeTo(this.config.initialSize);
55755             }
55756         }
55757         this.fireEvent("paneladded", this, panel);
55758         return panel;
55759     },
55760     
55761     /**
55762      * Returns true if the panel is in this region.
55763      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55764      * @return {Boolean}
55765      */
55766     hasPanel : function(panel){
55767         if(typeof panel == "object"){ // must be panel obj
55768             panel = panel.getId();
55769         }
55770         return this.getPanel(panel) ? true : false;
55771     },
55772     
55773     /**
55774      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55775      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55776      * @param {Boolean} preservePanel Overrides the config preservePanel option
55777      * @return {Roo.ContentPanel} The panel that was removed
55778      */
55779     remove : function(panel, preservePanel){
55780         panel = this.getPanel(panel);
55781         if(!panel){
55782             return null;
55783         }
55784         var e = {};
55785         this.fireEvent("beforeremove", this, panel, e);
55786         if(e.cancel === true){
55787             return null;
55788         }
55789         var panelId = panel.getId();
55790         this.panels.removeKey(panelId);
55791         return panel;
55792     },
55793     
55794     /**
55795      * Returns the panel specified or null if it's not in this region.
55796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55797      * @return {Roo.ContentPanel}
55798      */
55799     getPanel : function(id){
55800         if(typeof id == "object"){ // must be panel obj
55801             return id;
55802         }
55803         return this.panels.get(id);
55804     },
55805     
55806     /**
55807      * Returns this regions position (north/south/east/west/center).
55808      * @return {String} 
55809      */
55810     getPosition: function(){
55811         return this.position;    
55812     }
55813 });/*
55814  * Based on:
55815  * Ext JS Library 1.1.1
55816  * Copyright(c) 2006-2007, Ext JS, LLC.
55817  *
55818  * Originally Released Under LGPL - original licence link has changed is not relivant.
55819  *
55820  * Fork - LGPL
55821  * <script type="text/javascript">
55822  */
55823  
55824 /**
55825  * @class Roo.LayoutRegion
55826  * @extends Roo.BasicLayoutRegion
55827  * This class represents a region in a layout manager.
55828  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55829  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55830  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55831  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55832  * @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})
55833  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55834  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55835  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55836  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55837  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55838  * @cfg {String}    title           The title for the region (overrides panel titles)
55839  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55840  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55841  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55842  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55843  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55844  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55845  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55846  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55847  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55848  * @cfg {Boolean}   showPin         True to show a pin button
55849  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55850  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55851  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55852  * @cfg {Number}    width           For East/West panels
55853  * @cfg {Number}    height          For North/South panels
55854  * @cfg {Boolean}   split           To show the splitter
55855  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55856  */
55857 Roo.LayoutRegion = function(mgr, config, pos){
55858     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55859     var dh = Roo.DomHelper;
55860     /** This region's container element 
55861     * @type Roo.Element */
55862     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55863     /** This region's title element 
55864     * @type Roo.Element */
55865
55866     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55867         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55868         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55869     ]}, true);
55870     this.titleEl.enableDisplayMode();
55871     /** This region's title text element 
55872     * @type HTMLElement */
55873     this.titleTextEl = this.titleEl.dom.firstChild;
55874     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55875     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55876     this.closeBtn.enableDisplayMode();
55877     this.closeBtn.on("click", this.closeClicked, this);
55878     this.closeBtn.hide();
55879
55880     this.createBody(config);
55881     this.visible = true;
55882     this.collapsed = false;
55883
55884     if(config.hideWhenEmpty){
55885         this.hide();
55886         this.on("paneladded", this.validateVisibility, this);
55887         this.on("panelremoved", this.validateVisibility, this);
55888     }
55889     this.applyConfig(config);
55890 };
55891
55892 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55893
55894     createBody : function(){
55895         /** This region's body element 
55896         * @type Roo.Element */
55897         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55898     },
55899
55900     applyConfig : function(c){
55901         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55902             var dh = Roo.DomHelper;
55903             if(c.titlebar !== false){
55904                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55905                 this.collapseBtn.on("click", this.collapse, this);
55906                 this.collapseBtn.enableDisplayMode();
55907
55908                 if(c.showPin === true || this.showPin){
55909                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55910                     this.stickBtn.enableDisplayMode();
55911                     this.stickBtn.on("click", this.expand, this);
55912                     this.stickBtn.hide();
55913                 }
55914             }
55915             /** This region's collapsed element
55916             * @type Roo.Element */
55917             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55918                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55919             ]}, true);
55920             if(c.floatable !== false){
55921                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55922                this.collapsedEl.on("click", this.collapseClick, this);
55923             }
55924
55925             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55926                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55927                    id: "message", unselectable: "on", style:{"float":"left"}});
55928                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55929              }
55930             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55931             this.expandBtn.on("click", this.expand, this);
55932         }
55933         if(this.collapseBtn){
55934             this.collapseBtn.setVisible(c.collapsible == true);
55935         }
55936         this.cmargins = c.cmargins || this.cmargins ||
55937                          (this.position == "west" || this.position == "east" ?
55938                              {top: 0, left: 2, right:2, bottom: 0} :
55939                              {top: 2, left: 0, right:0, bottom: 2});
55940         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55941         this.bottomTabs = c.tabPosition != "top";
55942         this.autoScroll = c.autoScroll || false;
55943         if(this.autoScroll){
55944             this.bodyEl.setStyle("overflow", "auto");
55945         }else{
55946             this.bodyEl.setStyle("overflow", "hidden");
55947         }
55948         //if(c.titlebar !== false){
55949             if((!c.titlebar && !c.title) || c.titlebar === false){
55950                 this.titleEl.hide();
55951             }else{
55952                 this.titleEl.show();
55953                 if(c.title){
55954                     this.titleTextEl.innerHTML = c.title;
55955                 }
55956             }
55957         //}
55958         this.duration = c.duration || .30;
55959         this.slideDuration = c.slideDuration || .45;
55960         this.config = c;
55961         if(c.collapsed){
55962             this.collapse(true);
55963         }
55964         if(c.hidden){
55965             this.hide();
55966         }
55967     },
55968     /**
55969      * Returns true if this region is currently visible.
55970      * @return {Boolean}
55971      */
55972     isVisible : function(){
55973         return this.visible;
55974     },
55975
55976     /**
55977      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55978      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55979      */
55980     setCollapsedTitle : function(title){
55981         title = title || "&#160;";
55982         if(this.collapsedTitleTextEl){
55983             this.collapsedTitleTextEl.innerHTML = title;
55984         }
55985     },
55986
55987     getBox : function(){
55988         var b;
55989         if(!this.collapsed){
55990             b = this.el.getBox(false, true);
55991         }else{
55992             b = this.collapsedEl.getBox(false, true);
55993         }
55994         return b;
55995     },
55996
55997     getMargins : function(){
55998         return this.collapsed ? this.cmargins : this.margins;
55999     },
56000
56001     highlight : function(){
56002         this.el.addClass("x-layout-panel-dragover");
56003     },
56004
56005     unhighlight : function(){
56006         this.el.removeClass("x-layout-panel-dragover");
56007     },
56008
56009     updateBox : function(box){
56010         this.box = box;
56011         if(!this.collapsed){
56012             this.el.dom.style.left = box.x + "px";
56013             this.el.dom.style.top = box.y + "px";
56014             this.updateBody(box.width, box.height);
56015         }else{
56016             this.collapsedEl.dom.style.left = box.x + "px";
56017             this.collapsedEl.dom.style.top = box.y + "px";
56018             this.collapsedEl.setSize(box.width, box.height);
56019         }
56020         if(this.tabs){
56021             this.tabs.autoSizeTabs();
56022         }
56023     },
56024
56025     updateBody : function(w, h){
56026         if(w !== null){
56027             this.el.setWidth(w);
56028             w -= this.el.getBorderWidth("rl");
56029             if(this.config.adjustments){
56030                 w += this.config.adjustments[0];
56031             }
56032         }
56033         if(h !== null){
56034             this.el.setHeight(h);
56035             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56036             h -= this.el.getBorderWidth("tb");
56037             if(this.config.adjustments){
56038                 h += this.config.adjustments[1];
56039             }
56040             this.bodyEl.setHeight(h);
56041             if(this.tabs){
56042                 h = this.tabs.syncHeight(h);
56043             }
56044         }
56045         if(this.panelSize){
56046             w = w !== null ? w : this.panelSize.width;
56047             h = h !== null ? h : this.panelSize.height;
56048         }
56049         if(this.activePanel){
56050             var el = this.activePanel.getEl();
56051             w = w !== null ? w : el.getWidth();
56052             h = h !== null ? h : el.getHeight();
56053             this.panelSize = {width: w, height: h};
56054             this.activePanel.setSize(w, h);
56055         }
56056         if(Roo.isIE && this.tabs){
56057             this.tabs.el.repaint();
56058         }
56059     },
56060
56061     /**
56062      * Returns the container element for this region.
56063      * @return {Roo.Element}
56064      */
56065     getEl : function(){
56066         return this.el;
56067     },
56068
56069     /**
56070      * Hides this region.
56071      */
56072     hide : function(){
56073         if(!this.collapsed){
56074             this.el.dom.style.left = "-2000px";
56075             this.el.hide();
56076         }else{
56077             this.collapsedEl.dom.style.left = "-2000px";
56078             this.collapsedEl.hide();
56079         }
56080         this.visible = false;
56081         this.fireEvent("visibilitychange", this, false);
56082     },
56083
56084     /**
56085      * Shows this region if it was previously hidden.
56086      */
56087     show : function(){
56088         if(!this.collapsed){
56089             this.el.show();
56090         }else{
56091             this.collapsedEl.show();
56092         }
56093         this.visible = true;
56094         this.fireEvent("visibilitychange", this, true);
56095     },
56096
56097     closeClicked : function(){
56098         if(this.activePanel){
56099             this.remove(this.activePanel);
56100         }
56101     },
56102
56103     collapseClick : function(e){
56104         if(this.isSlid){
56105            e.stopPropagation();
56106            this.slideIn();
56107         }else{
56108            e.stopPropagation();
56109            this.slideOut();
56110         }
56111     },
56112
56113     /**
56114      * Collapses this region.
56115      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56116      */
56117     collapse : function(skipAnim, skipCheck){
56118         if(this.collapsed) {
56119             return;
56120         }
56121         
56122         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56123             
56124             this.collapsed = true;
56125             if(this.split){
56126                 this.split.el.hide();
56127             }
56128             if(this.config.animate && skipAnim !== true){
56129                 this.fireEvent("invalidated", this);
56130                 this.animateCollapse();
56131             }else{
56132                 this.el.setLocation(-20000,-20000);
56133                 this.el.hide();
56134                 this.collapsedEl.show();
56135                 this.fireEvent("collapsed", this);
56136                 this.fireEvent("invalidated", this);
56137             }
56138         }
56139         
56140     },
56141
56142     animateCollapse : function(){
56143         // overridden
56144     },
56145
56146     /**
56147      * Expands this region if it was previously collapsed.
56148      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56149      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56150      */
56151     expand : function(e, skipAnim){
56152         if(e) {
56153             e.stopPropagation();
56154         }
56155         if(!this.collapsed || this.el.hasActiveFx()) {
56156             return;
56157         }
56158         if(this.isSlid){
56159             this.afterSlideIn();
56160             skipAnim = true;
56161         }
56162         this.collapsed = false;
56163         if(this.config.animate && skipAnim !== true){
56164             this.animateExpand();
56165         }else{
56166             this.el.show();
56167             if(this.split){
56168                 this.split.el.show();
56169             }
56170             this.collapsedEl.setLocation(-2000,-2000);
56171             this.collapsedEl.hide();
56172             this.fireEvent("invalidated", this);
56173             this.fireEvent("expanded", this);
56174         }
56175     },
56176
56177     animateExpand : function(){
56178         // overridden
56179     },
56180
56181     initTabs : function()
56182     {
56183         this.bodyEl.setStyle("overflow", "hidden");
56184         var ts = new Roo.TabPanel(
56185                 this.bodyEl.dom,
56186                 {
56187                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56188                     disableTooltips: this.config.disableTabTips,
56189                     toolbar : this.config.toolbar
56190                 }
56191         );
56192         if(this.config.hideTabs){
56193             ts.stripWrap.setDisplayed(false);
56194         }
56195         this.tabs = ts;
56196         ts.resizeTabs = this.config.resizeTabs === true;
56197         ts.minTabWidth = this.config.minTabWidth || 40;
56198         ts.maxTabWidth = this.config.maxTabWidth || 250;
56199         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56200         ts.monitorResize = false;
56201         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56202         ts.bodyEl.addClass('x-layout-tabs-body');
56203         this.panels.each(this.initPanelAsTab, this);
56204     },
56205
56206     initPanelAsTab : function(panel){
56207         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56208                     this.config.closeOnTab && panel.isClosable());
56209         if(panel.tabTip !== undefined){
56210             ti.setTooltip(panel.tabTip);
56211         }
56212         ti.on("activate", function(){
56213               this.setActivePanel(panel);
56214         }, this);
56215         if(this.config.closeOnTab){
56216             ti.on("beforeclose", function(t, e){
56217                 e.cancel = true;
56218                 this.remove(panel);
56219             }, this);
56220         }
56221         return ti;
56222     },
56223
56224     updatePanelTitle : function(panel, title){
56225         if(this.activePanel == panel){
56226             this.updateTitle(title);
56227         }
56228         if(this.tabs){
56229             var ti = this.tabs.getTab(panel.getEl().id);
56230             ti.setText(title);
56231             if(panel.tabTip !== undefined){
56232                 ti.setTooltip(panel.tabTip);
56233             }
56234         }
56235     },
56236
56237     updateTitle : function(title){
56238         if(this.titleTextEl && !this.config.title){
56239             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56240         }
56241     },
56242
56243     setActivePanel : function(panel){
56244         panel = this.getPanel(panel);
56245         if(this.activePanel && this.activePanel != panel){
56246             this.activePanel.setActiveState(false);
56247         }
56248         this.activePanel = panel;
56249         panel.setActiveState(true);
56250         if(this.panelSize){
56251             panel.setSize(this.panelSize.width, this.panelSize.height);
56252         }
56253         if(this.closeBtn){
56254             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56255         }
56256         this.updateTitle(panel.getTitle());
56257         if(this.tabs){
56258             this.fireEvent("invalidated", this);
56259         }
56260         this.fireEvent("panelactivated", this, panel);
56261     },
56262
56263     /**
56264      * Shows the specified panel.
56265      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56266      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56267      */
56268     showPanel : function(panel)
56269     {
56270         panel = this.getPanel(panel);
56271         if(panel){
56272             if(this.tabs){
56273                 var tab = this.tabs.getTab(panel.getEl().id);
56274                 if(tab.isHidden()){
56275                     this.tabs.unhideTab(tab.id);
56276                 }
56277                 tab.activate();
56278             }else{
56279                 this.setActivePanel(panel);
56280             }
56281         }
56282         return panel;
56283     },
56284
56285     /**
56286      * Get the active panel for this region.
56287      * @return {Roo.ContentPanel} The active panel or null
56288      */
56289     getActivePanel : function(){
56290         return this.activePanel;
56291     },
56292
56293     validateVisibility : function(){
56294         if(this.panels.getCount() < 1){
56295             this.updateTitle("&#160;");
56296             this.closeBtn.hide();
56297             this.hide();
56298         }else{
56299             if(!this.isVisible()){
56300                 this.show();
56301             }
56302         }
56303     },
56304
56305     /**
56306      * Adds the passed ContentPanel(s) to this region.
56307      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56308      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56309      */
56310     add : function(panel){
56311         if(arguments.length > 1){
56312             for(var i = 0, len = arguments.length; i < len; i++) {
56313                 this.add(arguments[i]);
56314             }
56315             return null;
56316         }
56317         if(this.hasPanel(panel)){
56318             this.showPanel(panel);
56319             return panel;
56320         }
56321         panel.setRegion(this);
56322         this.panels.add(panel);
56323         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56324             this.bodyEl.dom.appendChild(panel.getEl().dom);
56325             if(panel.background !== true){
56326                 this.setActivePanel(panel);
56327             }
56328             this.fireEvent("paneladded", this, panel);
56329             return panel;
56330         }
56331         if(!this.tabs){
56332             this.initTabs();
56333         }else{
56334             this.initPanelAsTab(panel);
56335         }
56336         if(panel.background !== true){
56337             this.tabs.activate(panel.getEl().id);
56338         }
56339         this.fireEvent("paneladded", this, panel);
56340         return panel;
56341     },
56342
56343     /**
56344      * Hides the tab for the specified panel.
56345      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56346      */
56347     hidePanel : function(panel){
56348         if(this.tabs && (panel = this.getPanel(panel))){
56349             this.tabs.hideTab(panel.getEl().id);
56350         }
56351     },
56352
56353     /**
56354      * Unhides the tab for a previously hidden panel.
56355      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56356      */
56357     unhidePanel : function(panel){
56358         if(this.tabs && (panel = this.getPanel(panel))){
56359             this.tabs.unhideTab(panel.getEl().id);
56360         }
56361     },
56362
56363     clearPanels : function(){
56364         while(this.panels.getCount() > 0){
56365              this.remove(this.panels.first());
56366         }
56367     },
56368
56369     /**
56370      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56371      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56372      * @param {Boolean} preservePanel Overrides the config preservePanel option
56373      * @return {Roo.ContentPanel} The panel that was removed
56374      */
56375     remove : function(panel, preservePanel){
56376         panel = this.getPanel(panel);
56377         if(!panel){
56378             return null;
56379         }
56380         var e = {};
56381         this.fireEvent("beforeremove", this, panel, e);
56382         if(e.cancel === true){
56383             return null;
56384         }
56385         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56386         var panelId = panel.getId();
56387         this.panels.removeKey(panelId);
56388         if(preservePanel){
56389             document.body.appendChild(panel.getEl().dom);
56390         }
56391         if(this.tabs){
56392             this.tabs.removeTab(panel.getEl().id);
56393         }else if (!preservePanel){
56394             this.bodyEl.dom.removeChild(panel.getEl().dom);
56395         }
56396         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56397             var p = this.panels.first();
56398             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56399             tempEl.appendChild(p.getEl().dom);
56400             this.bodyEl.update("");
56401             this.bodyEl.dom.appendChild(p.getEl().dom);
56402             tempEl = null;
56403             this.updateTitle(p.getTitle());
56404             this.tabs = null;
56405             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56406             this.setActivePanel(p);
56407         }
56408         panel.setRegion(null);
56409         if(this.activePanel == panel){
56410             this.activePanel = null;
56411         }
56412         if(this.config.autoDestroy !== false && preservePanel !== true){
56413             try{panel.destroy();}catch(e){}
56414         }
56415         this.fireEvent("panelremoved", this, panel);
56416         return panel;
56417     },
56418
56419     /**
56420      * Returns the TabPanel component used by this region
56421      * @return {Roo.TabPanel}
56422      */
56423     getTabs : function(){
56424         return this.tabs;
56425     },
56426
56427     createTool : function(parentEl, className){
56428         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56429             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56430         btn.addClassOnOver("x-layout-tools-button-over");
56431         return btn;
56432     }
56433 });/*
56434  * Based on:
56435  * Ext JS Library 1.1.1
56436  * Copyright(c) 2006-2007, Ext JS, LLC.
56437  *
56438  * Originally Released Under LGPL - original licence link has changed is not relivant.
56439  *
56440  * Fork - LGPL
56441  * <script type="text/javascript">
56442  */
56443  
56444
56445
56446 /**
56447  * @class Roo.SplitLayoutRegion
56448  * @extends Roo.LayoutRegion
56449  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56450  */
56451 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56452     this.cursor = cursor;
56453     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56454 };
56455
56456 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56457     splitTip : "Drag to resize.",
56458     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56459     useSplitTips : false,
56460
56461     applyConfig : function(config){
56462         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56463         if(config.split){
56464             if(!this.split){
56465                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56466                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56467                 /** The SplitBar for this region 
56468                 * @type Roo.SplitBar */
56469                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56470                 this.split.on("moved", this.onSplitMove, this);
56471                 this.split.useShim = config.useShim === true;
56472                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56473                 if(this.useSplitTips){
56474                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56475                 }
56476                 if(config.collapsible){
56477                     this.split.el.on("dblclick", this.collapse,  this);
56478                 }
56479             }
56480             if(typeof config.minSize != "undefined"){
56481                 this.split.minSize = config.minSize;
56482             }
56483             if(typeof config.maxSize != "undefined"){
56484                 this.split.maxSize = config.maxSize;
56485             }
56486             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56487                 this.hideSplitter();
56488             }
56489         }
56490     },
56491
56492     getHMaxSize : function(){
56493          var cmax = this.config.maxSize || 10000;
56494          var center = this.mgr.getRegion("center");
56495          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56496     },
56497
56498     getVMaxSize : function(){
56499          var cmax = this.config.maxSize || 10000;
56500          var center = this.mgr.getRegion("center");
56501          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56502     },
56503
56504     onSplitMove : function(split, newSize){
56505         this.fireEvent("resized", this, newSize);
56506     },
56507     
56508     /** 
56509      * Returns the {@link Roo.SplitBar} for this region.
56510      * @return {Roo.SplitBar}
56511      */
56512     getSplitBar : function(){
56513         return this.split;
56514     },
56515     
56516     hide : function(){
56517         this.hideSplitter();
56518         Roo.SplitLayoutRegion.superclass.hide.call(this);
56519     },
56520
56521     hideSplitter : function(){
56522         if(this.split){
56523             this.split.el.setLocation(-2000,-2000);
56524             this.split.el.hide();
56525         }
56526     },
56527
56528     show : function(){
56529         if(this.split){
56530             this.split.el.show();
56531         }
56532         Roo.SplitLayoutRegion.superclass.show.call(this);
56533     },
56534     
56535     beforeSlide: function(){
56536         if(Roo.isGecko){// firefox overflow auto bug workaround
56537             this.bodyEl.clip();
56538             if(this.tabs) {
56539                 this.tabs.bodyEl.clip();
56540             }
56541             if(this.activePanel){
56542                 this.activePanel.getEl().clip();
56543                 
56544                 if(this.activePanel.beforeSlide){
56545                     this.activePanel.beforeSlide();
56546                 }
56547             }
56548         }
56549     },
56550     
56551     afterSlide : function(){
56552         if(Roo.isGecko){// firefox overflow auto bug workaround
56553             this.bodyEl.unclip();
56554             if(this.tabs) {
56555                 this.tabs.bodyEl.unclip();
56556             }
56557             if(this.activePanel){
56558                 this.activePanel.getEl().unclip();
56559                 if(this.activePanel.afterSlide){
56560                     this.activePanel.afterSlide();
56561                 }
56562             }
56563         }
56564     },
56565
56566     initAutoHide : function(){
56567         if(this.autoHide !== false){
56568             if(!this.autoHideHd){
56569                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56570                 this.autoHideHd = {
56571                     "mouseout": function(e){
56572                         if(!e.within(this.el, true)){
56573                             st.delay(500);
56574                         }
56575                     },
56576                     "mouseover" : function(e){
56577                         st.cancel();
56578                     },
56579                     scope : this
56580                 };
56581             }
56582             this.el.on(this.autoHideHd);
56583         }
56584     },
56585
56586     clearAutoHide : function(){
56587         if(this.autoHide !== false){
56588             this.el.un("mouseout", this.autoHideHd.mouseout);
56589             this.el.un("mouseover", this.autoHideHd.mouseover);
56590         }
56591     },
56592
56593     clearMonitor : function(){
56594         Roo.get(document).un("click", this.slideInIf, this);
56595     },
56596
56597     // these names are backwards but not changed for compat
56598     slideOut : function(){
56599         if(this.isSlid || this.el.hasActiveFx()){
56600             return;
56601         }
56602         this.isSlid = true;
56603         if(this.collapseBtn){
56604             this.collapseBtn.hide();
56605         }
56606         this.closeBtnState = this.closeBtn.getStyle('display');
56607         this.closeBtn.hide();
56608         if(this.stickBtn){
56609             this.stickBtn.show();
56610         }
56611         this.el.show();
56612         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56613         this.beforeSlide();
56614         this.el.setStyle("z-index", 10001);
56615         this.el.slideIn(this.getSlideAnchor(), {
56616             callback: function(){
56617                 this.afterSlide();
56618                 this.initAutoHide();
56619                 Roo.get(document).on("click", this.slideInIf, this);
56620                 this.fireEvent("slideshow", this);
56621             },
56622             scope: this,
56623             block: true
56624         });
56625     },
56626
56627     afterSlideIn : function(){
56628         this.clearAutoHide();
56629         this.isSlid = false;
56630         this.clearMonitor();
56631         this.el.setStyle("z-index", "");
56632         if(this.collapseBtn){
56633             this.collapseBtn.show();
56634         }
56635         this.closeBtn.setStyle('display', this.closeBtnState);
56636         if(this.stickBtn){
56637             this.stickBtn.hide();
56638         }
56639         this.fireEvent("slidehide", this);
56640     },
56641
56642     slideIn : function(cb){
56643         if(!this.isSlid || this.el.hasActiveFx()){
56644             Roo.callback(cb);
56645             return;
56646         }
56647         this.isSlid = false;
56648         this.beforeSlide();
56649         this.el.slideOut(this.getSlideAnchor(), {
56650             callback: function(){
56651                 this.el.setLeftTop(-10000, -10000);
56652                 this.afterSlide();
56653                 this.afterSlideIn();
56654                 Roo.callback(cb);
56655             },
56656             scope: this,
56657             block: true
56658         });
56659     },
56660     
56661     slideInIf : function(e){
56662         if(!e.within(this.el)){
56663             this.slideIn();
56664         }
56665     },
56666
56667     animateCollapse : function(){
56668         this.beforeSlide();
56669         this.el.setStyle("z-index", 20000);
56670         var anchor = this.getSlideAnchor();
56671         this.el.slideOut(anchor, {
56672             callback : function(){
56673                 this.el.setStyle("z-index", "");
56674                 this.collapsedEl.slideIn(anchor, {duration:.3});
56675                 this.afterSlide();
56676                 this.el.setLocation(-10000,-10000);
56677                 this.el.hide();
56678                 this.fireEvent("collapsed", this);
56679             },
56680             scope: this,
56681             block: true
56682         });
56683     },
56684
56685     animateExpand : function(){
56686         this.beforeSlide();
56687         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56688         this.el.setStyle("z-index", 20000);
56689         this.collapsedEl.hide({
56690             duration:.1
56691         });
56692         this.el.slideIn(this.getSlideAnchor(), {
56693             callback : function(){
56694                 this.el.setStyle("z-index", "");
56695                 this.afterSlide();
56696                 if(this.split){
56697                     this.split.el.show();
56698                 }
56699                 this.fireEvent("invalidated", this);
56700                 this.fireEvent("expanded", this);
56701             },
56702             scope: this,
56703             block: true
56704         });
56705     },
56706
56707     anchors : {
56708         "west" : "left",
56709         "east" : "right",
56710         "north" : "top",
56711         "south" : "bottom"
56712     },
56713
56714     sanchors : {
56715         "west" : "l",
56716         "east" : "r",
56717         "north" : "t",
56718         "south" : "b"
56719     },
56720
56721     canchors : {
56722         "west" : "tl-tr",
56723         "east" : "tr-tl",
56724         "north" : "tl-bl",
56725         "south" : "bl-tl"
56726     },
56727
56728     getAnchor : function(){
56729         return this.anchors[this.position];
56730     },
56731
56732     getCollapseAnchor : function(){
56733         return this.canchors[this.position];
56734     },
56735
56736     getSlideAnchor : function(){
56737         return this.sanchors[this.position];
56738     },
56739
56740     getAlignAdj : function(){
56741         var cm = this.cmargins;
56742         switch(this.position){
56743             case "west":
56744                 return [0, 0];
56745             break;
56746             case "east":
56747                 return [0, 0];
56748             break;
56749             case "north":
56750                 return [0, 0];
56751             break;
56752             case "south":
56753                 return [0, 0];
56754             break;
56755         }
56756     },
56757
56758     getExpandAdj : function(){
56759         var c = this.collapsedEl, cm = this.cmargins;
56760         switch(this.position){
56761             case "west":
56762                 return [-(cm.right+c.getWidth()+cm.left), 0];
56763             break;
56764             case "east":
56765                 return [cm.right+c.getWidth()+cm.left, 0];
56766             break;
56767             case "north":
56768                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56769             break;
56770             case "south":
56771                 return [0, cm.top+cm.bottom+c.getHeight()];
56772             break;
56773         }
56774     }
56775 });/*
56776  * Based on:
56777  * Ext JS Library 1.1.1
56778  * Copyright(c) 2006-2007, Ext JS, LLC.
56779  *
56780  * Originally Released Under LGPL - original licence link has changed is not relivant.
56781  *
56782  * Fork - LGPL
56783  * <script type="text/javascript">
56784  */
56785 /*
56786  * These classes are private internal classes
56787  */
56788 Roo.CenterLayoutRegion = function(mgr, config){
56789     Roo.LayoutRegion.call(this, mgr, config, "center");
56790     this.visible = true;
56791     this.minWidth = config.minWidth || 20;
56792     this.minHeight = config.minHeight || 20;
56793 };
56794
56795 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56796     hide : function(){
56797         // center panel can't be hidden
56798     },
56799     
56800     show : function(){
56801         // center panel can't be hidden
56802     },
56803     
56804     getMinWidth: function(){
56805         return this.minWidth;
56806     },
56807     
56808     getMinHeight: function(){
56809         return this.minHeight;
56810     }
56811 });
56812
56813
56814 Roo.NorthLayoutRegion = function(mgr, config){
56815     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56816     if(this.split){
56817         this.split.placement = Roo.SplitBar.TOP;
56818         this.split.orientation = Roo.SplitBar.VERTICAL;
56819         this.split.el.addClass("x-layout-split-v");
56820     }
56821     var size = config.initialSize || config.height;
56822     if(typeof size != "undefined"){
56823         this.el.setHeight(size);
56824     }
56825 };
56826 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56827     orientation: Roo.SplitBar.VERTICAL,
56828     getBox : function(){
56829         if(this.collapsed){
56830             return this.collapsedEl.getBox();
56831         }
56832         var box = this.el.getBox();
56833         if(this.split){
56834             box.height += this.split.el.getHeight();
56835         }
56836         return box;
56837     },
56838     
56839     updateBox : function(box){
56840         if(this.split && !this.collapsed){
56841             box.height -= this.split.el.getHeight();
56842             this.split.el.setLeft(box.x);
56843             this.split.el.setTop(box.y+box.height);
56844             this.split.el.setWidth(box.width);
56845         }
56846         if(this.collapsed){
56847             this.updateBody(box.width, null);
56848         }
56849         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56850     }
56851 });
56852
56853 Roo.SouthLayoutRegion = function(mgr, config){
56854     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56855     if(this.split){
56856         this.split.placement = Roo.SplitBar.BOTTOM;
56857         this.split.orientation = Roo.SplitBar.VERTICAL;
56858         this.split.el.addClass("x-layout-split-v");
56859     }
56860     var size = config.initialSize || config.height;
56861     if(typeof size != "undefined"){
56862         this.el.setHeight(size);
56863     }
56864 };
56865 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56866     orientation: Roo.SplitBar.VERTICAL,
56867     getBox : function(){
56868         if(this.collapsed){
56869             return this.collapsedEl.getBox();
56870         }
56871         var box = this.el.getBox();
56872         if(this.split){
56873             var sh = this.split.el.getHeight();
56874             box.height += sh;
56875             box.y -= sh;
56876         }
56877         return box;
56878     },
56879     
56880     updateBox : function(box){
56881         if(this.split && !this.collapsed){
56882             var sh = this.split.el.getHeight();
56883             box.height -= sh;
56884             box.y += sh;
56885             this.split.el.setLeft(box.x);
56886             this.split.el.setTop(box.y-sh);
56887             this.split.el.setWidth(box.width);
56888         }
56889         if(this.collapsed){
56890             this.updateBody(box.width, null);
56891         }
56892         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56893     }
56894 });
56895
56896 Roo.EastLayoutRegion = function(mgr, config){
56897     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56898     if(this.split){
56899         this.split.placement = Roo.SplitBar.RIGHT;
56900         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56901         this.split.el.addClass("x-layout-split-h");
56902     }
56903     var size = config.initialSize || config.width;
56904     if(typeof size != "undefined"){
56905         this.el.setWidth(size);
56906     }
56907 };
56908 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56909     orientation: Roo.SplitBar.HORIZONTAL,
56910     getBox : function(){
56911         if(this.collapsed){
56912             return this.collapsedEl.getBox();
56913         }
56914         var box = this.el.getBox();
56915         if(this.split){
56916             var sw = this.split.el.getWidth();
56917             box.width += sw;
56918             box.x -= sw;
56919         }
56920         return box;
56921     },
56922
56923     updateBox : function(box){
56924         if(this.split && !this.collapsed){
56925             var sw = this.split.el.getWidth();
56926             box.width -= sw;
56927             this.split.el.setLeft(box.x);
56928             this.split.el.setTop(box.y);
56929             this.split.el.setHeight(box.height);
56930             box.x += sw;
56931         }
56932         if(this.collapsed){
56933             this.updateBody(null, box.height);
56934         }
56935         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56936     }
56937 });
56938
56939 Roo.WestLayoutRegion = function(mgr, config){
56940     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56941     if(this.split){
56942         this.split.placement = Roo.SplitBar.LEFT;
56943         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56944         this.split.el.addClass("x-layout-split-h");
56945     }
56946     var size = config.initialSize || config.width;
56947     if(typeof size != "undefined"){
56948         this.el.setWidth(size);
56949     }
56950 };
56951 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56952     orientation: Roo.SplitBar.HORIZONTAL,
56953     getBox : function(){
56954         if(this.collapsed){
56955             return this.collapsedEl.getBox();
56956         }
56957         var box = this.el.getBox();
56958         if(this.split){
56959             box.width += this.split.el.getWidth();
56960         }
56961         return box;
56962     },
56963     
56964     updateBox : function(box){
56965         if(this.split && !this.collapsed){
56966             var sw = this.split.el.getWidth();
56967             box.width -= sw;
56968             this.split.el.setLeft(box.x+box.width);
56969             this.split.el.setTop(box.y);
56970             this.split.el.setHeight(box.height);
56971         }
56972         if(this.collapsed){
56973             this.updateBody(null, box.height);
56974         }
56975         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56976     }
56977 });
56978 /*
56979  * Based on:
56980  * Ext JS Library 1.1.1
56981  * Copyright(c) 2006-2007, Ext JS, LLC.
56982  *
56983  * Originally Released Under LGPL - original licence link has changed is not relivant.
56984  *
56985  * Fork - LGPL
56986  * <script type="text/javascript">
56987  */
56988  
56989  
56990 /*
56991  * Private internal class for reading and applying state
56992  */
56993 Roo.LayoutStateManager = function(layout){
56994      // default empty state
56995      this.state = {
56996         north: {},
56997         south: {},
56998         east: {},
56999         west: {}       
57000     };
57001 };
57002
57003 Roo.LayoutStateManager.prototype = {
57004     init : function(layout, provider){
57005         this.provider = provider;
57006         var state = provider.get(layout.id+"-layout-state");
57007         if(state){
57008             var wasUpdating = layout.isUpdating();
57009             if(!wasUpdating){
57010                 layout.beginUpdate();
57011             }
57012             for(var key in state){
57013                 if(typeof state[key] != "function"){
57014                     var rstate = state[key];
57015                     var r = layout.getRegion(key);
57016                     if(r && rstate){
57017                         if(rstate.size){
57018                             r.resizeTo(rstate.size);
57019                         }
57020                         if(rstate.collapsed == true){
57021                             r.collapse(true);
57022                         }else{
57023                             r.expand(null, true);
57024                         }
57025                     }
57026                 }
57027             }
57028             if(!wasUpdating){
57029                 layout.endUpdate();
57030             }
57031             this.state = state; 
57032         }
57033         this.layout = layout;
57034         layout.on("regionresized", this.onRegionResized, this);
57035         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57036         layout.on("regionexpanded", this.onRegionExpanded, this);
57037     },
57038     
57039     storeState : function(){
57040         this.provider.set(this.layout.id+"-layout-state", this.state);
57041     },
57042     
57043     onRegionResized : function(region, newSize){
57044         this.state[region.getPosition()].size = newSize;
57045         this.storeState();
57046     },
57047     
57048     onRegionCollapsed : function(region){
57049         this.state[region.getPosition()].collapsed = true;
57050         this.storeState();
57051     },
57052     
57053     onRegionExpanded : function(region){
57054         this.state[region.getPosition()].collapsed = false;
57055         this.storeState();
57056     }
57057 };/*
57058  * Based on:
57059  * Ext JS Library 1.1.1
57060  * Copyright(c) 2006-2007, Ext JS, LLC.
57061  *
57062  * Originally Released Under LGPL - original licence link has changed is not relivant.
57063  *
57064  * Fork - LGPL
57065  * <script type="text/javascript">
57066  */
57067 /**
57068  * @class Roo.ContentPanel
57069  * @extends Roo.util.Observable
57070  * @children Roo.form.Form Roo.JsonView Roo.View
57071  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57072  * A basic ContentPanel element.
57073  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57074  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57075  * @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
57076  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57077  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57078  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57079  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57080  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57081  * @cfg {String} title          The title for this panel
57082  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57083  * @cfg {String} url            Calls {@link #setUrl} with this value
57084  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57085  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57086  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57087  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57088  * @cfg {String}    style  Extra style to add to the content panel
57089  * @cfg {Roo.menu.Menu} menu  popup menu
57090
57091  * @constructor
57092  * Create a new ContentPanel.
57093  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57094  * @param {String/Object} config A string to set only the title or a config object
57095  * @param {String} content (optional) Set the HTML content for this panel
57096  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57097  */
57098 Roo.ContentPanel = function(el, config, content){
57099     
57100      
57101     /*
57102     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57103         config = el;
57104         el = Roo.id();
57105     }
57106     if (config && config.parentLayout) { 
57107         el = config.parentLayout.el.createChild(); 
57108     }
57109     */
57110     if(el.autoCreate){ // xtype is available if this is called from factory
57111         config = el;
57112         el = Roo.id();
57113     }
57114     this.el = Roo.get(el);
57115     if(!this.el && config && config.autoCreate){
57116         if(typeof config.autoCreate == "object"){
57117             if(!config.autoCreate.id){
57118                 config.autoCreate.id = config.id||el;
57119             }
57120             this.el = Roo.DomHelper.append(document.body,
57121                         config.autoCreate, true);
57122         }else{
57123             this.el = Roo.DomHelper.append(document.body,
57124                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57125         }
57126     }
57127     
57128     
57129     this.closable = false;
57130     this.loaded = false;
57131     this.active = false;
57132     if(typeof config == "string"){
57133         this.title = config;
57134     }else{
57135         Roo.apply(this, config);
57136     }
57137     
57138     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57139         this.wrapEl = this.el.wrap();
57140         this.toolbar.container = this.el.insertSibling(false, 'before');
57141         this.toolbar = new Roo.Toolbar(this.toolbar);
57142     }
57143     
57144     // xtype created footer. - not sure if will work as we normally have to render first..
57145     if (this.footer && !this.footer.el && this.footer.xtype) {
57146         if (!this.wrapEl) {
57147             this.wrapEl = this.el.wrap();
57148         }
57149     
57150         this.footer.container = this.wrapEl.createChild();
57151          
57152         this.footer = Roo.factory(this.footer, Roo);
57153         
57154     }
57155     
57156     if(this.resizeEl){
57157         this.resizeEl = Roo.get(this.resizeEl, true);
57158     }else{
57159         this.resizeEl = this.el;
57160     }
57161     // handle view.xtype
57162     
57163  
57164     
57165     
57166     this.addEvents({
57167         /**
57168          * @event activate
57169          * Fires when this panel is activated. 
57170          * @param {Roo.ContentPanel} this
57171          */
57172         "activate" : true,
57173         /**
57174          * @event deactivate
57175          * Fires when this panel is activated. 
57176          * @param {Roo.ContentPanel} this
57177          */
57178         "deactivate" : true,
57179
57180         /**
57181          * @event resize
57182          * Fires when this panel is resized if fitToFrame is true.
57183          * @param {Roo.ContentPanel} this
57184          * @param {Number} width The width after any component adjustments
57185          * @param {Number} height The height after any component adjustments
57186          */
57187         "resize" : true,
57188         
57189          /**
57190          * @event render
57191          * Fires when this tab is created
57192          * @param {Roo.ContentPanel} this
57193          */
57194         "render" : true
57195          
57196         
57197     });
57198     
57199
57200     
57201     
57202     if(this.autoScroll){
57203         this.resizeEl.setStyle("overflow", "auto");
57204     } else {
57205         // fix randome scrolling
57206         this.el.on('scroll', function() {
57207             Roo.log('fix random scolling');
57208             this.scrollTo('top',0); 
57209         });
57210     }
57211     content = content || this.content;
57212     if(content){
57213         this.setContent(content);
57214     }
57215     if(config && config.url){
57216         this.setUrl(this.url, this.params, this.loadOnce);
57217     }
57218     
57219     
57220     
57221     Roo.ContentPanel.superclass.constructor.call(this);
57222     
57223     if (this.view && typeof(this.view.xtype) != 'undefined') {
57224         this.view.el = this.el.appendChild(document.createElement("div"));
57225         this.view = Roo.factory(this.view); 
57226         this.view.render  &&  this.view.render(false, '');  
57227     }
57228     
57229     
57230     this.fireEvent('render', this);
57231 };
57232
57233 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57234     tabTip:'',
57235     setRegion : function(region){
57236         this.region = region;
57237         if(region){
57238            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57239         }else{
57240            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57241         } 
57242     },
57243     
57244     /**
57245      * Returns the toolbar for this Panel if one was configured. 
57246      * @return {Roo.Toolbar} 
57247      */
57248     getToolbar : function(){
57249         return this.toolbar;
57250     },
57251     
57252     setActiveState : function(active){
57253         this.active = active;
57254         if(!active){
57255             this.fireEvent("deactivate", this);
57256         }else{
57257             this.fireEvent("activate", this);
57258         }
57259     },
57260     /**
57261      * Updates this panel's element
57262      * @param {String} content The new content
57263      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57264     */
57265     setContent : function(content, loadScripts){
57266         this.el.update(content, loadScripts);
57267     },
57268
57269     ignoreResize : function(w, h){
57270         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57271             return true;
57272         }else{
57273             this.lastSize = {width: w, height: h};
57274             return false;
57275         }
57276     },
57277     /**
57278      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57279      * @return {Roo.UpdateManager} The UpdateManager
57280      */
57281     getUpdateManager : function(){
57282         return this.el.getUpdateManager();
57283     },
57284      /**
57285      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57286      * @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:
57287 <pre><code>
57288 panel.load({
57289     url: "your-url.php",
57290     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57291     callback: yourFunction,
57292     scope: yourObject, //(optional scope)
57293     discardUrl: false,
57294     nocache: false,
57295     text: "Loading...",
57296     timeout: 30,
57297     scripts: false
57298 });
57299 </code></pre>
57300      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57301      * 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.
57302      * @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}
57303      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57304      * @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.
57305      * @return {Roo.ContentPanel} this
57306      */
57307     load : function(){
57308         var um = this.el.getUpdateManager();
57309         um.update.apply(um, arguments);
57310         return this;
57311     },
57312
57313
57314     /**
57315      * 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.
57316      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57317      * @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)
57318      * @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)
57319      * @return {Roo.UpdateManager} The UpdateManager
57320      */
57321     setUrl : function(url, params, loadOnce){
57322         if(this.refreshDelegate){
57323             this.removeListener("activate", this.refreshDelegate);
57324         }
57325         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57326         this.on("activate", this.refreshDelegate);
57327         return this.el.getUpdateManager();
57328     },
57329     
57330     _handleRefresh : function(url, params, loadOnce){
57331         if(!loadOnce || !this.loaded){
57332             var updater = this.el.getUpdateManager();
57333             updater.update(url, params, this._setLoaded.createDelegate(this));
57334         }
57335     },
57336     
57337     _setLoaded : function(){
57338         this.loaded = true;
57339     }, 
57340     
57341     /**
57342      * Returns this panel's id
57343      * @return {String} 
57344      */
57345     getId : function(){
57346         return this.el.id;
57347     },
57348     
57349     /** 
57350      * Returns this panel's element - used by regiosn to add.
57351      * @return {Roo.Element} 
57352      */
57353     getEl : function(){
57354         return this.wrapEl || this.el;
57355     },
57356     
57357     adjustForComponents : function(width, height)
57358     {
57359         //Roo.log('adjustForComponents ');
57360         if(this.resizeEl != this.el){
57361             width -= this.el.getFrameWidth('lr');
57362             height -= this.el.getFrameWidth('tb');
57363         }
57364         if(this.toolbar){
57365             var te = this.toolbar.getEl();
57366             height -= te.getHeight();
57367             te.setWidth(width);
57368         }
57369         if(this.footer){
57370             var te = this.footer.getEl();
57371             //Roo.log("footer:" + te.getHeight());
57372             
57373             height -= te.getHeight();
57374             te.setWidth(width);
57375         }
57376         
57377         
57378         if(this.adjustments){
57379             width += this.adjustments[0];
57380             height += this.adjustments[1];
57381         }
57382         return {"width": width, "height": height};
57383     },
57384     
57385     setSize : function(width, height){
57386         if(this.fitToFrame && !this.ignoreResize(width, height)){
57387             if(this.fitContainer && this.resizeEl != this.el){
57388                 this.el.setSize(width, height);
57389             }
57390             var size = this.adjustForComponents(width, height);
57391             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57392             this.fireEvent('resize', this, size.width, size.height);
57393         }
57394     },
57395     
57396     /**
57397      * Returns this panel's title
57398      * @return {String} 
57399      */
57400     getTitle : function(){
57401         return this.title;
57402     },
57403     
57404     /**
57405      * Set this panel's title
57406      * @param {String} title
57407      */
57408     setTitle : function(title){
57409         this.title = title;
57410         if(this.region){
57411             this.region.updatePanelTitle(this, title);
57412         }
57413     },
57414     
57415     /**
57416      * Returns true is this panel was configured to be closable
57417      * @return {Boolean} 
57418      */
57419     isClosable : function(){
57420         return this.closable;
57421     },
57422     
57423     beforeSlide : function(){
57424         this.el.clip();
57425         this.resizeEl.clip();
57426     },
57427     
57428     afterSlide : function(){
57429         this.el.unclip();
57430         this.resizeEl.unclip();
57431     },
57432     
57433     /**
57434      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57435      *   Will fail silently if the {@link #setUrl} method has not been called.
57436      *   This does not activate the panel, just updates its content.
57437      */
57438     refresh : function(){
57439         if(this.refreshDelegate){
57440            this.loaded = false;
57441            this.refreshDelegate();
57442         }
57443     },
57444     
57445     /**
57446      * Destroys this panel
57447      */
57448     destroy : function(){
57449         this.el.removeAllListeners();
57450         var tempEl = document.createElement("span");
57451         tempEl.appendChild(this.el.dom);
57452         tempEl.innerHTML = "";
57453         this.el.remove();
57454         this.el = null;
57455     },
57456     
57457     /**
57458      * form - if the content panel contains a form - this is a reference to it.
57459      * @type {Roo.form.Form}
57460      */
57461     form : false,
57462     /**
57463      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57464      *    This contains a reference to it.
57465      * @type {Roo.View}
57466      */
57467     view : false,
57468     
57469       /**
57470      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57471      * <pre><code>
57472
57473 layout.addxtype({
57474        xtype : 'Form',
57475        items: [ .... ]
57476    }
57477 );
57478
57479 </code></pre>
57480      * @param {Object} cfg Xtype definition of item to add.
57481      */
57482     
57483     addxtype : function(cfg) {
57484         // add form..
57485         if (cfg.xtype.match(/^Form$/)) {
57486             
57487             var el;
57488             //if (this.footer) {
57489             //    el = this.footer.container.insertSibling(false, 'before');
57490             //} else {
57491                 el = this.el.createChild();
57492             //}
57493
57494             this.form = new  Roo.form.Form(cfg);
57495             
57496             
57497             if ( this.form.allItems.length) {
57498                 this.form.render(el.dom);
57499             }
57500             return this.form;
57501         }
57502         // should only have one of theses..
57503         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57504             // views.. should not be just added - used named prop 'view''
57505             
57506             cfg.el = this.el.appendChild(document.createElement("div"));
57507             // factory?
57508             
57509             var ret = new Roo.factory(cfg);
57510              
57511              ret.render && ret.render(false, ''); // render blank..
57512             this.view = ret;
57513             return ret;
57514         }
57515         return false;
57516     }
57517 });
57518
57519 /**
57520  * @class Roo.GridPanel
57521  * @extends Roo.ContentPanel
57522  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57523  * @constructor
57524  * Create a new GridPanel.
57525  * @cfg {Roo.grid.Grid} grid The grid for this panel
57526  */
57527 Roo.GridPanel = function(grid, config){
57528     
57529     // universal ctor...
57530     if (typeof(grid.grid) != 'undefined') {
57531         config = grid;
57532         grid = config.grid;
57533     }
57534     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57535         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57536         
57537     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57538     
57539     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57540     
57541     if(this.toolbar){
57542         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57543     }
57544     // xtype created footer. - not sure if will work as we normally have to render first..
57545     if (this.footer && !this.footer.el && this.footer.xtype) {
57546         
57547         this.footer.container = this.grid.getView().getFooterPanel(true);
57548         this.footer.dataSource = this.grid.dataSource;
57549         this.footer = Roo.factory(this.footer, Roo);
57550         
57551     }
57552     
57553     grid.monitorWindowResize = false; // turn off autosizing
57554     grid.autoHeight = false;
57555     grid.autoWidth = false;
57556     this.grid = grid;
57557     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57558 };
57559
57560 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57561     getId : function(){
57562         return this.grid.id;
57563     },
57564     
57565     /**
57566      * Returns the grid for this panel
57567      * @return {Roo.grid.Grid} 
57568      */
57569     getGrid : function(){
57570         return this.grid;    
57571     },
57572     
57573     setSize : function(width, height){
57574         if(!this.ignoreResize(width, height)){
57575             var grid = this.grid;
57576             var size = this.adjustForComponents(width, height);
57577             grid.getGridEl().setSize(size.width, size.height);
57578             grid.autoSize();
57579         }
57580     },
57581     
57582     beforeSlide : function(){
57583         this.grid.getView().scroller.clip();
57584     },
57585     
57586     afterSlide : function(){
57587         this.grid.getView().scroller.unclip();
57588     },
57589     
57590     destroy : function(){
57591         this.grid.destroy();
57592         delete this.grid;
57593         Roo.GridPanel.superclass.destroy.call(this); 
57594     }
57595 });
57596
57597
57598 /**
57599  * @class Roo.NestedLayoutPanel
57600  * @extends Roo.ContentPanel
57601  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57602  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57603  *
57604  * 
57605  * @constructor
57606  * Create a new NestedLayoutPanel.
57607  * 
57608  * 
57609  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57610  * @param {String/Object} config A string to set only the title or a config object
57611  */
57612 Roo.NestedLayoutPanel = function(layout, config)
57613 {
57614     // construct with only one argument..
57615     /* FIXME - implement nicer consturctors
57616     if (layout.layout) {
57617         config = layout;
57618         layout = config.layout;
57619         delete config.layout;
57620     }
57621     if (layout.xtype && !layout.getEl) {
57622         // then layout needs constructing..
57623         layout = Roo.factory(layout, Roo);
57624     }
57625     */
57626     
57627     
57628     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57629     
57630     layout.monitorWindowResize = false; // turn off autosizing
57631     this.layout = layout;
57632     this.layout.getEl().addClass("x-layout-nested-layout");
57633     
57634     
57635     
57636     
57637 };
57638
57639 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57640
57641     setSize : function(width, height){
57642         if(!this.ignoreResize(width, height)){
57643             var size = this.adjustForComponents(width, height);
57644             var el = this.layout.getEl();
57645             el.setSize(size.width, size.height);
57646             var touch = el.dom.offsetWidth;
57647             this.layout.layout();
57648             // ie requires a double layout on the first pass
57649             if(Roo.isIE && !this.initialized){
57650                 this.initialized = true;
57651                 this.layout.layout();
57652             }
57653         }
57654     },
57655     
57656     // activate all subpanels if not currently active..
57657     
57658     setActiveState : function(active){
57659         this.active = active;
57660         if(!active){
57661             this.fireEvent("deactivate", this);
57662             return;
57663         }
57664         
57665         this.fireEvent("activate", this);
57666         // not sure if this should happen before or after..
57667         if (!this.layout) {
57668             return; // should not happen..
57669         }
57670         var reg = false;
57671         for (var r in this.layout.regions) {
57672             reg = this.layout.getRegion(r);
57673             if (reg.getActivePanel()) {
57674                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57675                 reg.setActivePanel(reg.getActivePanel());
57676                 continue;
57677             }
57678             if (!reg.panels.length) {
57679                 continue;
57680             }
57681             reg.showPanel(reg.getPanel(0));
57682         }
57683         
57684         
57685         
57686         
57687     },
57688     
57689     /**
57690      * Returns the nested BorderLayout for this panel
57691      * @return {Roo.BorderLayout} 
57692      */
57693     getLayout : function(){
57694         return this.layout;
57695     },
57696     
57697      /**
57698      * Adds a xtype elements to the layout of the nested panel
57699      * <pre><code>
57700
57701 panel.addxtype({
57702        xtype : 'ContentPanel',
57703        region: 'west',
57704        items: [ .... ]
57705    }
57706 );
57707
57708 panel.addxtype({
57709         xtype : 'NestedLayoutPanel',
57710         region: 'west',
57711         layout: {
57712            center: { },
57713            west: { }   
57714         },
57715         items : [ ... list of content panels or nested layout panels.. ]
57716    }
57717 );
57718 </code></pre>
57719      * @param {Object} cfg Xtype definition of item to add.
57720      */
57721     addxtype : function(cfg) {
57722         return this.layout.addxtype(cfg);
57723     
57724     }
57725 });
57726
57727 Roo.ScrollPanel = function(el, config, content){
57728     config = config || {};
57729     config.fitToFrame = true;
57730     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57731     
57732     this.el.dom.style.overflow = "hidden";
57733     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57734     this.el.removeClass("x-layout-inactive-content");
57735     this.el.on("mousewheel", this.onWheel, this);
57736
57737     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57738     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57739     up.unselectable(); down.unselectable();
57740     up.on("click", this.scrollUp, this);
57741     down.on("click", this.scrollDown, this);
57742     up.addClassOnOver("x-scroller-btn-over");
57743     down.addClassOnOver("x-scroller-btn-over");
57744     up.addClassOnClick("x-scroller-btn-click");
57745     down.addClassOnClick("x-scroller-btn-click");
57746     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57747
57748     this.resizeEl = this.el;
57749     this.el = wrap; this.up = up; this.down = down;
57750 };
57751
57752 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57753     increment : 100,
57754     wheelIncrement : 5,
57755     scrollUp : function(){
57756         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57757     },
57758
57759     scrollDown : function(){
57760         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57761     },
57762
57763     afterScroll : function(){
57764         var el = this.resizeEl;
57765         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57766         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57767         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57768     },
57769
57770     setSize : function(){
57771         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57772         this.afterScroll();
57773     },
57774
57775     onWheel : function(e){
57776         var d = e.getWheelDelta();
57777         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57778         this.afterScroll();
57779         e.stopEvent();
57780     },
57781
57782     setContent : function(content, loadScripts){
57783         this.resizeEl.update(content, loadScripts);
57784     }
57785
57786 });
57787
57788
57789
57790 /**
57791  * @class Roo.TreePanel
57792  * @extends Roo.ContentPanel
57793  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57794  * Treepanel component
57795  * 
57796  * @constructor
57797  * Create a new TreePanel. - defaults to fit/scoll contents.
57798  * @param {String/Object} config A string to set only the panel's title, or a config object
57799  */
57800 Roo.TreePanel = function(config){
57801     var el = config.el;
57802     var tree = config.tree;
57803     delete config.tree; 
57804     delete config.el; // hopefull!
57805     
57806     // wrapper for IE7 strict & safari scroll issue
57807     
57808     var treeEl = el.createChild();
57809     config.resizeEl = treeEl;
57810     
57811     
57812     
57813     Roo.TreePanel.superclass.constructor.call(this, el, config);
57814  
57815  
57816     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57817     //console.log(tree);
57818     this.on('activate', function()
57819     {
57820         if (this.tree.rendered) {
57821             return;
57822         }
57823         //console.log('render tree');
57824         this.tree.render();
57825     });
57826     // this should not be needed.. - it's actually the 'el' that resizes?
57827     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57828     
57829     //this.on('resize',  function (cp, w, h) {
57830     //        this.tree.innerCt.setWidth(w);
57831     //        this.tree.innerCt.setHeight(h);
57832     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57833     //});
57834
57835         
57836     
57837 };
57838
57839 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57840     fitToFrame : true,
57841     autoScroll : true,
57842     /*
57843      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57844      */
57845     tree : false
57846
57847 });
57848
57849
57850
57851
57852
57853
57854
57855
57856
57857
57858
57859 /*
57860  * Based on:
57861  * Ext JS Library 1.1.1
57862  * Copyright(c) 2006-2007, Ext JS, LLC.
57863  *
57864  * Originally Released Under LGPL - original licence link has changed is not relivant.
57865  *
57866  * Fork - LGPL
57867  * <script type="text/javascript">
57868  */
57869  
57870
57871 /**
57872  * @class Roo.ReaderLayout
57873  * @extends Roo.BorderLayout
57874  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57875  * center region containing two nested regions (a top one for a list view and one for item preview below),
57876  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57877  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57878  * expedites the setup of the overall layout and regions for this common application style.
57879  * Example:
57880  <pre><code>
57881 var reader = new Roo.ReaderLayout();
57882 var CP = Roo.ContentPanel;  // shortcut for adding
57883
57884 reader.beginUpdate();
57885 reader.add("north", new CP("north", "North"));
57886 reader.add("west", new CP("west", {title: "West"}));
57887 reader.add("east", new CP("east", {title: "East"}));
57888
57889 reader.regions.listView.add(new CP("listView", "List"));
57890 reader.regions.preview.add(new CP("preview", "Preview"));
57891 reader.endUpdate();
57892 </code></pre>
57893 * @constructor
57894 * Create a new ReaderLayout
57895 * @param {Object} config Configuration options
57896 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57897 * document.body if omitted)
57898 */
57899 Roo.ReaderLayout = function(config, renderTo){
57900     var c = config || {size:{}};
57901     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57902         north: c.north !== false ? Roo.apply({
57903             split:false,
57904             initialSize: 32,
57905             titlebar: false
57906         }, c.north) : false,
57907         west: c.west !== false ? Roo.apply({
57908             split:true,
57909             initialSize: 200,
57910             minSize: 175,
57911             maxSize: 400,
57912             titlebar: true,
57913             collapsible: true,
57914             animate: true,
57915             margins:{left:5,right:0,bottom:5,top:5},
57916             cmargins:{left:5,right:5,bottom:5,top:5}
57917         }, c.west) : false,
57918         east: c.east !== false ? Roo.apply({
57919             split:true,
57920             initialSize: 200,
57921             minSize: 175,
57922             maxSize: 400,
57923             titlebar: true,
57924             collapsible: true,
57925             animate: true,
57926             margins:{left:0,right:5,bottom:5,top:5},
57927             cmargins:{left:5,right:5,bottom:5,top:5}
57928         }, c.east) : false,
57929         center: Roo.apply({
57930             tabPosition: 'top',
57931             autoScroll:false,
57932             closeOnTab: true,
57933             titlebar:false,
57934             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57935         }, c.center)
57936     });
57937
57938     this.el.addClass('x-reader');
57939
57940     this.beginUpdate();
57941
57942     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57943         south: c.preview !== false ? Roo.apply({
57944             split:true,
57945             initialSize: 200,
57946             minSize: 100,
57947             autoScroll:true,
57948             collapsible:true,
57949             titlebar: true,
57950             cmargins:{top:5,left:0, right:0, bottom:0}
57951         }, c.preview) : false,
57952         center: Roo.apply({
57953             autoScroll:false,
57954             titlebar:false,
57955             minHeight:200
57956         }, c.listView)
57957     });
57958     this.add('center', new Roo.NestedLayoutPanel(inner,
57959             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57960
57961     this.endUpdate();
57962
57963     this.regions.preview = inner.getRegion('south');
57964     this.regions.listView = inner.getRegion('center');
57965 };
57966
57967 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57968  * Based on:
57969  * Ext JS Library 1.1.1
57970  * Copyright(c) 2006-2007, Ext JS, LLC.
57971  *
57972  * Originally Released Under LGPL - original licence link has changed is not relivant.
57973  *
57974  * Fork - LGPL
57975  * <script type="text/javascript">
57976  */
57977  
57978 /**
57979  * @class Roo.grid.Grid
57980  * @extends Roo.util.Observable
57981  * This class represents the primary interface of a component based grid control.
57982  * <br><br>Usage:<pre><code>
57983  var grid = new Roo.grid.Grid("my-container-id", {
57984      ds: myDataStore,
57985      cm: myColModel,
57986      selModel: mySelectionModel,
57987      autoSizeColumns: true,
57988      monitorWindowResize: false,
57989      trackMouseOver: true
57990  });
57991  // set any options
57992  grid.render();
57993  * </code></pre>
57994  * <b>Common Problems:</b><br/>
57995  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57996  * element will correct this<br/>
57997  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57998  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57999  * are unpredictable.<br/>
58000  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
58001  * grid to calculate dimensions/offsets.<br/>
58002   * @constructor
58003  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58004  * The container MUST have some type of size defined for the grid to fill. The container will be
58005  * automatically set to position relative if it isn't already.
58006  * @param {Object} config A config object that sets properties on this grid.
58007  */
58008 Roo.grid.Grid = function(container, config){
58009         // initialize the container
58010         this.container = Roo.get(container);
58011         this.container.update("");
58012         this.container.setStyle("overflow", "hidden");
58013     this.container.addClass('x-grid-container');
58014
58015     this.id = this.container.id;
58016
58017     Roo.apply(this, config);
58018     // check and correct shorthanded configs
58019     if(this.ds){
58020         this.dataSource = this.ds;
58021         delete this.ds;
58022     }
58023     if(this.cm){
58024         this.colModel = this.cm;
58025         delete this.cm;
58026     }
58027     if(this.sm){
58028         this.selModel = this.sm;
58029         delete this.sm;
58030     }
58031
58032     if (this.selModel) {
58033         this.selModel = Roo.factory(this.selModel, Roo.grid);
58034         this.sm = this.selModel;
58035         this.sm.xmodule = this.xmodule || false;
58036     }
58037     if (typeof(this.colModel.config) == 'undefined') {
58038         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58039         this.cm = this.colModel;
58040         this.cm.xmodule = this.xmodule || false;
58041     }
58042     if (this.dataSource) {
58043         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58044         this.ds = this.dataSource;
58045         this.ds.xmodule = this.xmodule || false;
58046          
58047     }
58048     
58049     
58050     
58051     if(this.width){
58052         this.container.setWidth(this.width);
58053     }
58054
58055     if(this.height){
58056         this.container.setHeight(this.height);
58057     }
58058     /** @private */
58059         this.addEvents({
58060         // raw events
58061         /**
58062          * @event click
58063          * The raw click event for the entire grid.
58064          * @param {Roo.EventObject} e
58065          */
58066         "click" : true,
58067         /**
58068          * @event dblclick
58069          * The raw dblclick event for the entire grid.
58070          * @param {Roo.EventObject} e
58071          */
58072         "dblclick" : true,
58073         /**
58074          * @event contextmenu
58075          * The raw contextmenu event for the entire grid.
58076          * @param {Roo.EventObject} e
58077          */
58078         "contextmenu" : true,
58079         /**
58080          * @event mousedown
58081          * The raw mousedown event for the entire grid.
58082          * @param {Roo.EventObject} e
58083          */
58084         "mousedown" : true,
58085         /**
58086          * @event mouseup
58087          * The raw mouseup event for the entire grid.
58088          * @param {Roo.EventObject} e
58089          */
58090         "mouseup" : true,
58091         /**
58092          * @event mouseover
58093          * The raw mouseover event for the entire grid.
58094          * @param {Roo.EventObject} e
58095          */
58096         "mouseover" : true,
58097         /**
58098          * @event mouseout
58099          * The raw mouseout event for the entire grid.
58100          * @param {Roo.EventObject} e
58101          */
58102         "mouseout" : true,
58103         /**
58104          * @event keypress
58105          * The raw keypress event for the entire grid.
58106          * @param {Roo.EventObject} e
58107          */
58108         "keypress" : true,
58109         /**
58110          * @event keydown
58111          * The raw keydown event for the entire grid.
58112          * @param {Roo.EventObject} e
58113          */
58114         "keydown" : true,
58115
58116         // custom events
58117
58118         /**
58119          * @event cellclick
58120          * Fires when a cell is clicked
58121          * @param {Grid} this
58122          * @param {Number} rowIndex
58123          * @param {Number} columnIndex
58124          * @param {Roo.EventObject} e
58125          */
58126         "cellclick" : true,
58127         /**
58128          * @event celldblclick
58129          * Fires when a cell is double clicked
58130          * @param {Grid} this
58131          * @param {Number} rowIndex
58132          * @param {Number} columnIndex
58133          * @param {Roo.EventObject} e
58134          */
58135         "celldblclick" : true,
58136         /**
58137          * @event rowclick
58138          * Fires when a row is clicked
58139          * @param {Grid} this
58140          * @param {Number} rowIndex
58141          * @param {Roo.EventObject} e
58142          */
58143         "rowclick" : true,
58144         /**
58145          * @event rowdblclick
58146          * Fires when a row is double clicked
58147          * @param {Grid} this
58148          * @param {Number} rowIndex
58149          * @param {Roo.EventObject} e
58150          */
58151         "rowdblclick" : true,
58152         /**
58153          * @event headerclick
58154          * Fires when a header is clicked
58155          * @param {Grid} this
58156          * @param {Number} columnIndex
58157          * @param {Roo.EventObject} e
58158          */
58159         "headerclick" : true,
58160         /**
58161          * @event headerdblclick
58162          * Fires when a header cell is double clicked
58163          * @param {Grid} this
58164          * @param {Number} columnIndex
58165          * @param {Roo.EventObject} e
58166          */
58167         "headerdblclick" : true,
58168         /**
58169          * @event rowcontextmenu
58170          * Fires when a row is right clicked
58171          * @param {Grid} this
58172          * @param {Number} rowIndex
58173          * @param {Roo.EventObject} e
58174          */
58175         "rowcontextmenu" : true,
58176         /**
58177          * @event cellcontextmenu
58178          * Fires when a cell is right clicked
58179          * @param {Grid} this
58180          * @param {Number} rowIndex
58181          * @param {Number} cellIndex
58182          * @param {Roo.EventObject} e
58183          */
58184          "cellcontextmenu" : true,
58185         /**
58186          * @event headercontextmenu
58187          * Fires when a header is right clicked
58188          * @param {Grid} this
58189          * @param {Number} columnIndex
58190          * @param {Roo.EventObject} e
58191          */
58192         "headercontextmenu" : true,
58193         /**
58194          * @event bodyscroll
58195          * Fires when the body element is scrolled
58196          * @param {Number} scrollLeft
58197          * @param {Number} scrollTop
58198          */
58199         "bodyscroll" : true,
58200         /**
58201          * @event columnresize
58202          * Fires when the user resizes a column
58203          * @param {Number} columnIndex
58204          * @param {Number} newSize
58205          */
58206         "columnresize" : true,
58207         /**
58208          * @event columnmove
58209          * Fires when the user moves a column
58210          * @param {Number} oldIndex
58211          * @param {Number} newIndex
58212          */
58213         "columnmove" : true,
58214         /**
58215          * @event startdrag
58216          * Fires when row(s) start being dragged
58217          * @param {Grid} this
58218          * @param {Roo.GridDD} dd The drag drop object
58219          * @param {event} e The raw browser event
58220          */
58221         "startdrag" : true,
58222         /**
58223          * @event enddrag
58224          * Fires when a drag operation is complete
58225          * @param {Grid} this
58226          * @param {Roo.GridDD} dd The drag drop object
58227          * @param {event} e The raw browser event
58228          */
58229         "enddrag" : true,
58230         /**
58231          * @event dragdrop
58232          * Fires when dragged row(s) are dropped on a valid DD target
58233          * @param {Grid} this
58234          * @param {Roo.GridDD} dd The drag drop object
58235          * @param {String} targetId The target drag drop object
58236          * @param {event} e The raw browser event
58237          */
58238         "dragdrop" : true,
58239         /**
58240          * @event dragover
58241          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58242          * @param {Grid} this
58243          * @param {Roo.GridDD} dd The drag drop object
58244          * @param {String} targetId The target drag drop object
58245          * @param {event} e The raw browser event
58246          */
58247         "dragover" : true,
58248         /**
58249          * @event dragenter
58250          *  Fires when the dragged row(s) first cross another DD target while being dragged
58251          * @param {Grid} this
58252          * @param {Roo.GridDD} dd The drag drop object
58253          * @param {String} targetId The target drag drop object
58254          * @param {event} e The raw browser event
58255          */
58256         "dragenter" : true,
58257         /**
58258          * @event dragout
58259          * Fires when the dragged row(s) leave another DD target while being dragged
58260          * @param {Grid} this
58261          * @param {Roo.GridDD} dd The drag drop object
58262          * @param {String} targetId The target drag drop object
58263          * @param {event} e The raw browser event
58264          */
58265         "dragout" : true,
58266         /**
58267          * @event rowclass
58268          * Fires when a row is rendered, so you can change add a style to it.
58269          * @param {GridView} gridview   The grid view
58270          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58271          */
58272         'rowclass' : true,
58273
58274         /**
58275          * @event render
58276          * Fires when the grid is rendered
58277          * @param {Grid} grid
58278          */
58279         'render' : true
58280     });
58281
58282     Roo.grid.Grid.superclass.constructor.call(this);
58283 };
58284 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58285     
58286     /**
58287          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58288          */
58289         /**
58290          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58291          */
58292         /**
58293          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58294          */
58295         /**
58296          * @cfg {Roo.grid.Store} ds The data store for the grid
58297          */
58298         /**
58299          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58300          */
58301         /**
58302      * @cfg {String} ddGroup - drag drop group.
58303      */
58304       /**
58305      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58306      */
58307
58308     /**
58309      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58310      */
58311     minColumnWidth : 25,
58312
58313     /**
58314      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58315      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58316      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58317      */
58318     autoSizeColumns : false,
58319
58320     /**
58321      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58322      */
58323     autoSizeHeaders : true,
58324
58325     /**
58326      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58327      */
58328     monitorWindowResize : true,
58329
58330     /**
58331      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58332      * rows measured to get a columns size. Default is 0 (all rows).
58333      */
58334     maxRowsToMeasure : 0,
58335
58336     /**
58337      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58338      */
58339     trackMouseOver : true,
58340
58341     /**
58342     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58343     */
58344       /**
58345     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58346     */
58347     
58348     /**
58349     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58350     */
58351     enableDragDrop : false,
58352     
58353     /**
58354     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58355     */
58356     enableColumnMove : true,
58357     
58358     /**
58359     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58360     */
58361     enableColumnHide : true,
58362     
58363     /**
58364     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58365     */
58366     enableRowHeightSync : false,
58367     
58368     /**
58369     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58370     */
58371     stripeRows : true,
58372     
58373     /**
58374     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58375     */
58376     autoHeight : false,
58377
58378     /**
58379      * @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.
58380      */
58381     autoExpandColumn : false,
58382
58383     /**
58384     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58385     * Default is 50.
58386     */
58387     autoExpandMin : 50,
58388
58389     /**
58390     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58391     */
58392     autoExpandMax : 1000,
58393
58394     /**
58395     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58396     */
58397     view : null,
58398
58399     /**
58400     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58401     */
58402     loadMask : false,
58403     /**
58404     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58405     */
58406     dropTarget: false,
58407      /**
58408     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58409     */ 
58410     sortColMenu : false,
58411     
58412     // private
58413     rendered : false,
58414
58415     /**
58416     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58417     * of a fixed width. Default is false.
58418     */
58419     /**
58420     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58421     */
58422     
58423     
58424     /**
58425     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58426     * %0 is replaced with the number of selected rows.
58427     */
58428     ddText : "{0} selected row{1}",
58429     
58430     
58431     /**
58432      * Called once after all setup has been completed and the grid is ready to be rendered.
58433      * @return {Roo.grid.Grid} this
58434      */
58435     render : function()
58436     {
58437         var c = this.container;
58438         // try to detect autoHeight/width mode
58439         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58440             this.autoHeight = true;
58441         }
58442         var view = this.getView();
58443         view.init(this);
58444
58445         c.on("click", this.onClick, this);
58446         c.on("dblclick", this.onDblClick, this);
58447         c.on("contextmenu", this.onContextMenu, this);
58448         c.on("keydown", this.onKeyDown, this);
58449         if (Roo.isTouch) {
58450             c.on("touchstart", this.onTouchStart, this);
58451         }
58452
58453         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58454
58455         this.getSelectionModel().init(this);
58456
58457         view.render();
58458
58459         if(this.loadMask){
58460             this.loadMask = new Roo.LoadMask(this.container,
58461                     Roo.apply({store:this.dataSource}, this.loadMask));
58462         }
58463         
58464         
58465         if (this.toolbar && this.toolbar.xtype) {
58466             this.toolbar.container = this.getView().getHeaderPanel(true);
58467             this.toolbar = new Roo.Toolbar(this.toolbar);
58468         }
58469         if (this.footer && this.footer.xtype) {
58470             this.footer.dataSource = this.getDataSource();
58471             this.footer.container = this.getView().getFooterPanel(true);
58472             this.footer = Roo.factory(this.footer, Roo);
58473         }
58474         if (this.dropTarget && this.dropTarget.xtype) {
58475             delete this.dropTarget.xtype;
58476             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58477         }
58478         
58479         
58480         this.rendered = true;
58481         this.fireEvent('render', this);
58482         return this;
58483     },
58484
58485     /**
58486      * Reconfigures the grid to use a different Store and Column Model.
58487      * The View will be bound to the new objects and refreshed.
58488      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58489      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58490      */
58491     reconfigure : function(dataSource, colModel){
58492         if(this.loadMask){
58493             this.loadMask.destroy();
58494             this.loadMask = new Roo.LoadMask(this.container,
58495                     Roo.apply({store:dataSource}, this.loadMask));
58496         }
58497         this.view.bind(dataSource, colModel);
58498         this.dataSource = dataSource;
58499         this.colModel = colModel;
58500         this.view.refresh(true);
58501     },
58502     /**
58503      * addColumns
58504      * Add's a column, default at the end..
58505      
58506      * @param {int} position to add (default end)
58507      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58508      */
58509     addColumns : function(pos, ar)
58510     {
58511         
58512         for (var i =0;i< ar.length;i++) {
58513             var cfg = ar[i];
58514             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58515             this.cm.lookup[cfg.id] = cfg;
58516         }
58517         
58518         
58519         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58520             pos = this.cm.config.length; //this.cm.config.push(cfg);
58521         } 
58522         pos = Math.max(0,pos);
58523         ar.unshift(0);
58524         ar.unshift(pos);
58525         this.cm.config.splice.apply(this.cm.config, ar);
58526         
58527         
58528         
58529         this.view.generateRules(this.cm);
58530         this.view.refresh(true);
58531         
58532     },
58533     
58534     
58535     
58536     
58537     // private
58538     onKeyDown : function(e){
58539         this.fireEvent("keydown", e);
58540     },
58541
58542     /**
58543      * Destroy this grid.
58544      * @param {Boolean} removeEl True to remove the element
58545      */
58546     destroy : function(removeEl, keepListeners){
58547         if(this.loadMask){
58548             this.loadMask.destroy();
58549         }
58550         var c = this.container;
58551         c.removeAllListeners();
58552         this.view.destroy();
58553         this.colModel.purgeListeners();
58554         if(!keepListeners){
58555             this.purgeListeners();
58556         }
58557         c.update("");
58558         if(removeEl === true){
58559             c.remove();
58560         }
58561     },
58562
58563     // private
58564     processEvent : function(name, e){
58565         // does this fire select???
58566         //Roo.log('grid:processEvent '  + name);
58567         
58568         if (name != 'touchstart' ) {
58569             this.fireEvent(name, e);    
58570         }
58571         
58572         var t = e.getTarget();
58573         var v = this.view;
58574         var header = v.findHeaderIndex(t);
58575         if(header !== false){
58576             var ename = name == 'touchstart' ? 'click' : name;
58577              
58578             this.fireEvent("header" + ename, this, header, e);
58579         }else{
58580             var row = v.findRowIndex(t);
58581             var cell = v.findCellIndex(t);
58582             if (name == 'touchstart') {
58583                 // first touch is always a click.
58584                 // hopefull this happens after selection is updated.?
58585                 name = false;
58586                 
58587                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58588                     var cs = this.selModel.getSelectedCell();
58589                     if (row == cs[0] && cell == cs[1]){
58590                         name = 'dblclick';
58591                     }
58592                 }
58593                 if (typeof(this.selModel.getSelections) != 'undefined') {
58594                     var cs = this.selModel.getSelections();
58595                     var ds = this.dataSource;
58596                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58597                         name = 'dblclick';
58598                     }
58599                 }
58600                 if (!name) {
58601                     return;
58602                 }
58603             }
58604             
58605             
58606             if(row !== false){
58607                 this.fireEvent("row" + name, this, row, e);
58608                 if(cell !== false){
58609                     this.fireEvent("cell" + name, this, row, cell, e);
58610                 }
58611             }
58612         }
58613     },
58614
58615     // private
58616     onClick : function(e){
58617         this.processEvent("click", e);
58618     },
58619    // private
58620     onTouchStart : function(e){
58621         this.processEvent("touchstart", e);
58622     },
58623
58624     // private
58625     onContextMenu : function(e, t){
58626         this.processEvent("contextmenu", e);
58627     },
58628
58629     // private
58630     onDblClick : function(e){
58631         this.processEvent("dblclick", e);
58632     },
58633
58634     // private
58635     walkCells : function(row, col, step, fn, scope){
58636         var cm = this.colModel, clen = cm.getColumnCount();
58637         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58638         if(step < 0){
58639             if(col < 0){
58640                 row--;
58641                 first = false;
58642             }
58643             while(row >= 0){
58644                 if(!first){
58645                     col = clen-1;
58646                 }
58647                 first = false;
58648                 while(col >= 0){
58649                     if(fn.call(scope || this, row, col, cm) === true){
58650                         return [row, col];
58651                     }
58652                     col--;
58653                 }
58654                 row--;
58655             }
58656         } else {
58657             if(col >= clen){
58658                 row++;
58659                 first = false;
58660             }
58661             while(row < rlen){
58662                 if(!first){
58663                     col = 0;
58664                 }
58665                 first = false;
58666                 while(col < clen){
58667                     if(fn.call(scope || this, row, col, cm) === true){
58668                         return [row, col];
58669                     }
58670                     col++;
58671                 }
58672                 row++;
58673             }
58674         }
58675         return null;
58676     },
58677
58678     // private
58679     getSelections : function(){
58680         return this.selModel.getSelections();
58681     },
58682
58683     /**
58684      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58685      * but if manual update is required this method will initiate it.
58686      */
58687     autoSize : function(){
58688         if(this.rendered){
58689             this.view.layout();
58690             if(this.view.adjustForScroll){
58691                 this.view.adjustForScroll();
58692             }
58693         }
58694     },
58695
58696     /**
58697      * Returns the grid's underlying element.
58698      * @return {Element} The element
58699      */
58700     getGridEl : function(){
58701         return this.container;
58702     },
58703
58704     // private for compatibility, overridden by editor grid
58705     stopEditing : function(){},
58706
58707     /**
58708      * Returns the grid's SelectionModel.
58709      * @return {SelectionModel}
58710      */
58711     getSelectionModel : function(){
58712         if(!this.selModel){
58713             this.selModel = new Roo.grid.RowSelectionModel();
58714         }
58715         return this.selModel;
58716     },
58717
58718     /**
58719      * Returns the grid's DataSource.
58720      * @return {DataSource}
58721      */
58722     getDataSource : function(){
58723         return this.dataSource;
58724     },
58725
58726     /**
58727      * Returns the grid's ColumnModel.
58728      * @return {ColumnModel}
58729      */
58730     getColumnModel : function(){
58731         return this.colModel;
58732     },
58733
58734     /**
58735      * Returns the grid's GridView object.
58736      * @return {GridView}
58737      */
58738     getView : function(){
58739         if(!this.view){
58740             this.view = new Roo.grid.GridView(this.viewConfig);
58741             this.relayEvents(this.view, [
58742                 "beforerowremoved", "beforerowsinserted",
58743                 "beforerefresh", "rowremoved",
58744                 "rowsinserted", "rowupdated" ,"refresh"
58745             ]);
58746         }
58747         return this.view;
58748     },
58749     /**
58750      * Called to get grid's drag proxy text, by default returns this.ddText.
58751      * Override this to put something different in the dragged text.
58752      * @return {String}
58753      */
58754     getDragDropText : function(){
58755         var count = this.selModel.getCount();
58756         return String.format(this.ddText, count, count == 1 ? '' : 's');
58757     }
58758 });
58759 /*
58760  * Based on:
58761  * Ext JS Library 1.1.1
58762  * Copyright(c) 2006-2007, Ext JS, LLC.
58763  *
58764  * Originally Released Under LGPL - original licence link has changed is not relivant.
58765  *
58766  * Fork - LGPL
58767  * <script type="text/javascript">
58768  */
58769  /**
58770  * @class Roo.grid.AbstractGridView
58771  * @extends Roo.util.Observable
58772  * @abstract
58773  * Abstract base class for grid Views
58774  * @constructor
58775  */
58776 Roo.grid.AbstractGridView = function(){
58777         this.grid = null;
58778         
58779         this.events = {
58780             "beforerowremoved" : true,
58781             "beforerowsinserted" : true,
58782             "beforerefresh" : true,
58783             "rowremoved" : true,
58784             "rowsinserted" : true,
58785             "rowupdated" : true,
58786             "refresh" : true
58787         };
58788     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58789 };
58790
58791 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58792     rowClass : "x-grid-row",
58793     cellClass : "x-grid-cell",
58794     tdClass : "x-grid-td",
58795     hdClass : "x-grid-hd",
58796     splitClass : "x-grid-hd-split",
58797     
58798     init: function(grid){
58799         this.grid = grid;
58800                 var cid = this.grid.getGridEl().id;
58801         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58802         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58803         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58804         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58805         },
58806         
58807     getColumnRenderers : function(){
58808         var renderers = [];
58809         var cm = this.grid.colModel;
58810         var colCount = cm.getColumnCount();
58811         for(var i = 0; i < colCount; i++){
58812             renderers[i] = cm.getRenderer(i);
58813         }
58814         return renderers;
58815     },
58816     
58817     getColumnIds : function(){
58818         var ids = [];
58819         var cm = this.grid.colModel;
58820         var colCount = cm.getColumnCount();
58821         for(var i = 0; i < colCount; i++){
58822             ids[i] = cm.getColumnId(i);
58823         }
58824         return ids;
58825     },
58826     
58827     getDataIndexes : function(){
58828         if(!this.indexMap){
58829             this.indexMap = this.buildIndexMap();
58830         }
58831         return this.indexMap.colToData;
58832     },
58833     
58834     getColumnIndexByDataIndex : function(dataIndex){
58835         if(!this.indexMap){
58836             this.indexMap = this.buildIndexMap();
58837         }
58838         return this.indexMap.dataToCol[dataIndex];
58839     },
58840     
58841     /**
58842      * Set a css style for a column dynamically. 
58843      * @param {Number} colIndex The index of the column
58844      * @param {String} name The css property name
58845      * @param {String} value The css value
58846      */
58847     setCSSStyle : function(colIndex, name, value){
58848         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58849         Roo.util.CSS.updateRule(selector, name, value);
58850     },
58851     
58852     generateRules : function(cm){
58853         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58854         Roo.util.CSS.removeStyleSheet(rulesId);
58855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58856             var cid = cm.getColumnId(i);
58857             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58858                          this.tdSelector, cid, " {\n}\n",
58859                          this.hdSelector, cid, " {\n}\n",
58860                          this.splitSelector, cid, " {\n}\n");
58861         }
58862         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58863     }
58864 });/*
58865  * Based on:
58866  * Ext JS Library 1.1.1
58867  * Copyright(c) 2006-2007, Ext JS, LLC.
58868  *
58869  * Originally Released Under LGPL - original licence link has changed is not relivant.
58870  *
58871  * Fork - LGPL
58872  * <script type="text/javascript">
58873  */
58874
58875 // private
58876 // This is a support class used internally by the Grid components
58877 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58878     this.grid = grid;
58879     this.view = grid.getView();
58880     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58881     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58882     if(hd2){
58883         this.setHandleElId(Roo.id(hd));
58884         this.setOuterHandleElId(Roo.id(hd2));
58885     }
58886     this.scroll = false;
58887 };
58888 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58889     maxDragWidth: 120,
58890     getDragData : function(e){
58891         var t = Roo.lib.Event.getTarget(e);
58892         var h = this.view.findHeaderCell(t);
58893         if(h){
58894             return {ddel: h.firstChild, header:h};
58895         }
58896         return false;
58897     },
58898
58899     onInitDrag : function(e){
58900         this.view.headersDisabled = true;
58901         var clone = this.dragData.ddel.cloneNode(true);
58902         clone.id = Roo.id();
58903         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58904         this.proxy.update(clone);
58905         return true;
58906     },
58907
58908     afterValidDrop : function(){
58909         var v = this.view;
58910         setTimeout(function(){
58911             v.headersDisabled = false;
58912         }, 50);
58913     },
58914
58915     afterInvalidDrop : function(){
58916         var v = this.view;
58917         setTimeout(function(){
58918             v.headersDisabled = false;
58919         }, 50);
58920     }
58921 });
58922 /*
58923  * Based on:
58924  * Ext JS Library 1.1.1
58925  * Copyright(c) 2006-2007, Ext JS, LLC.
58926  *
58927  * Originally Released Under LGPL - original licence link has changed is not relivant.
58928  *
58929  * Fork - LGPL
58930  * <script type="text/javascript">
58931  */
58932 // private
58933 // This is a support class used internally by the Grid components
58934 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58935     this.grid = grid;
58936     this.view = grid.getView();
58937     // split the proxies so they don't interfere with mouse events
58938     this.proxyTop = Roo.DomHelper.append(document.body, {
58939         cls:"col-move-top", html:"&#160;"
58940     }, true);
58941     this.proxyBottom = Roo.DomHelper.append(document.body, {
58942         cls:"col-move-bottom", html:"&#160;"
58943     }, true);
58944     this.proxyTop.hide = this.proxyBottom.hide = function(){
58945         this.setLeftTop(-100,-100);
58946         this.setStyle("visibility", "hidden");
58947     };
58948     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58949     // temporarily disabled
58950     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58951     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58952 };
58953 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58954     proxyOffsets : [-4, -9],
58955     fly: Roo.Element.fly,
58956
58957     getTargetFromEvent : function(e){
58958         var t = Roo.lib.Event.getTarget(e);
58959         var cindex = this.view.findCellIndex(t);
58960         if(cindex !== false){
58961             return this.view.getHeaderCell(cindex);
58962         }
58963         return null;
58964     },
58965
58966     nextVisible : function(h){
58967         var v = this.view, cm = this.grid.colModel;
58968         h = h.nextSibling;
58969         while(h){
58970             if(!cm.isHidden(v.getCellIndex(h))){
58971                 return h;
58972             }
58973             h = h.nextSibling;
58974         }
58975         return null;
58976     },
58977
58978     prevVisible : function(h){
58979         var v = this.view, cm = this.grid.colModel;
58980         h = h.prevSibling;
58981         while(h){
58982             if(!cm.isHidden(v.getCellIndex(h))){
58983                 return h;
58984             }
58985             h = h.prevSibling;
58986         }
58987         return null;
58988     },
58989
58990     positionIndicator : function(h, n, e){
58991         var x = Roo.lib.Event.getPageX(e);
58992         var r = Roo.lib.Dom.getRegion(n.firstChild);
58993         var px, pt, py = r.top + this.proxyOffsets[1];
58994         if((r.right - x) <= (r.right-r.left)/2){
58995             px = r.right+this.view.borderWidth;
58996             pt = "after";
58997         }else{
58998             px = r.left;
58999             pt = "before";
59000         }
59001         var oldIndex = this.view.getCellIndex(h);
59002         var newIndex = this.view.getCellIndex(n);
59003
59004         if(this.grid.colModel.isFixed(newIndex)){
59005             return false;
59006         }
59007
59008         var locked = this.grid.colModel.isLocked(newIndex);
59009
59010         if(pt == "after"){
59011             newIndex++;
59012         }
59013         if(oldIndex < newIndex){
59014             newIndex--;
59015         }
59016         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59017             return false;
59018         }
59019         px +=  this.proxyOffsets[0];
59020         this.proxyTop.setLeftTop(px, py);
59021         this.proxyTop.show();
59022         if(!this.bottomOffset){
59023             this.bottomOffset = this.view.mainHd.getHeight();
59024         }
59025         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59026         this.proxyBottom.show();
59027         return pt;
59028     },
59029
59030     onNodeEnter : function(n, dd, e, data){
59031         if(data.header != n){
59032             this.positionIndicator(data.header, n, e);
59033         }
59034     },
59035
59036     onNodeOver : function(n, dd, e, data){
59037         var result = false;
59038         if(data.header != n){
59039             result = this.positionIndicator(data.header, n, e);
59040         }
59041         if(!result){
59042             this.proxyTop.hide();
59043             this.proxyBottom.hide();
59044         }
59045         return result ? this.dropAllowed : this.dropNotAllowed;
59046     },
59047
59048     onNodeOut : function(n, dd, e, data){
59049         this.proxyTop.hide();
59050         this.proxyBottom.hide();
59051     },
59052
59053     onNodeDrop : function(n, dd, e, data){
59054         var h = data.header;
59055         if(h != n){
59056             var cm = this.grid.colModel;
59057             var x = Roo.lib.Event.getPageX(e);
59058             var r = Roo.lib.Dom.getRegion(n.firstChild);
59059             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59060             var oldIndex = this.view.getCellIndex(h);
59061             var newIndex = this.view.getCellIndex(n);
59062             var locked = cm.isLocked(newIndex);
59063             if(pt == "after"){
59064                 newIndex++;
59065             }
59066             if(oldIndex < newIndex){
59067                 newIndex--;
59068             }
59069             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59070                 return false;
59071             }
59072             cm.setLocked(oldIndex, locked, true);
59073             cm.moveColumn(oldIndex, newIndex);
59074             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59075             return true;
59076         }
59077         return false;
59078     }
59079 });
59080 /*
59081  * Based on:
59082  * Ext JS Library 1.1.1
59083  * Copyright(c) 2006-2007, Ext JS, LLC.
59084  *
59085  * Originally Released Under LGPL - original licence link has changed is not relivant.
59086  *
59087  * Fork - LGPL
59088  * <script type="text/javascript">
59089  */
59090   
59091 /**
59092  * @class Roo.grid.GridView
59093  * @extends Roo.util.Observable
59094  *
59095  * @constructor
59096  * @param {Object} config
59097  */
59098 Roo.grid.GridView = function(config){
59099     Roo.grid.GridView.superclass.constructor.call(this);
59100     this.el = null;
59101
59102     Roo.apply(this, config);
59103 };
59104
59105 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59106
59107     unselectable :  'unselectable="on"',
59108     unselectableCls :  'x-unselectable',
59109     
59110     
59111     rowClass : "x-grid-row",
59112
59113     cellClass : "x-grid-col",
59114
59115     tdClass : "x-grid-td",
59116
59117     hdClass : "x-grid-hd",
59118
59119     splitClass : "x-grid-split",
59120
59121     sortClasses : ["sort-asc", "sort-desc"],
59122
59123     enableMoveAnim : false,
59124
59125     hlColor: "C3DAF9",
59126
59127     dh : Roo.DomHelper,
59128
59129     fly : Roo.Element.fly,
59130
59131     css : Roo.util.CSS,
59132
59133     borderWidth: 1,
59134
59135     splitOffset: 3,
59136
59137     scrollIncrement : 22,
59138
59139     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59140
59141     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59142
59143     bind : function(ds, cm){
59144         if(this.ds){
59145             this.ds.un("load", this.onLoad, this);
59146             this.ds.un("datachanged", this.onDataChange, this);
59147             this.ds.un("add", this.onAdd, this);
59148             this.ds.un("remove", this.onRemove, this);
59149             this.ds.un("update", this.onUpdate, this);
59150             this.ds.un("clear", this.onClear, this);
59151         }
59152         if(ds){
59153             ds.on("load", this.onLoad, this);
59154             ds.on("datachanged", this.onDataChange, this);
59155             ds.on("add", this.onAdd, this);
59156             ds.on("remove", this.onRemove, this);
59157             ds.on("update", this.onUpdate, this);
59158             ds.on("clear", this.onClear, this);
59159         }
59160         this.ds = ds;
59161
59162         if(this.cm){
59163             this.cm.un("widthchange", this.onColWidthChange, this);
59164             this.cm.un("headerchange", this.onHeaderChange, this);
59165             this.cm.un("hiddenchange", this.onHiddenChange, this);
59166             this.cm.un("columnmoved", this.onColumnMove, this);
59167             this.cm.un("columnlockchange", this.onColumnLock, this);
59168         }
59169         if(cm){
59170             this.generateRules(cm);
59171             cm.on("widthchange", this.onColWidthChange, this);
59172             cm.on("headerchange", this.onHeaderChange, this);
59173             cm.on("hiddenchange", this.onHiddenChange, this);
59174             cm.on("columnmoved", this.onColumnMove, this);
59175             cm.on("columnlockchange", this.onColumnLock, this);
59176         }
59177         this.cm = cm;
59178     },
59179
59180     init: function(grid){
59181         Roo.grid.GridView.superclass.init.call(this, grid);
59182
59183         this.bind(grid.dataSource, grid.colModel);
59184
59185         grid.on("headerclick", this.handleHeaderClick, this);
59186
59187         if(grid.trackMouseOver){
59188             grid.on("mouseover", this.onRowOver, this);
59189             grid.on("mouseout", this.onRowOut, this);
59190         }
59191         grid.cancelTextSelection = function(){};
59192         this.gridId = grid.id;
59193
59194         var tpls = this.templates || {};
59195
59196         if(!tpls.master){
59197             tpls.master = new Roo.Template(
59198                '<div class="x-grid" hidefocus="true">',
59199                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59200                   '<div class="x-grid-topbar"></div>',
59201                   '<div class="x-grid-scroller"><div></div></div>',
59202                   '<div class="x-grid-locked">',
59203                       '<div class="x-grid-header">{lockedHeader}</div>',
59204                       '<div class="x-grid-body">{lockedBody}</div>',
59205                   "</div>",
59206                   '<div class="x-grid-viewport">',
59207                       '<div class="x-grid-header">{header}</div>',
59208                       '<div class="x-grid-body">{body}</div>',
59209                   "</div>",
59210                   '<div class="x-grid-bottombar"></div>',
59211                  
59212                   '<div class="x-grid-resize-proxy">&#160;</div>',
59213                "</div>"
59214             );
59215             tpls.master.disableformats = true;
59216         }
59217
59218         if(!tpls.header){
59219             tpls.header = new Roo.Template(
59220                '<table border="0" cellspacing="0" cellpadding="0">',
59221                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59222                "</table>{splits}"
59223             );
59224             tpls.header.disableformats = true;
59225         }
59226         tpls.header.compile();
59227
59228         if(!tpls.hcell){
59229             tpls.hcell = new Roo.Template(
59230                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59231                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59232                 "</div></td>"
59233              );
59234              tpls.hcell.disableFormats = true;
59235         }
59236         tpls.hcell.compile();
59237
59238         if(!tpls.hsplit){
59239             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59240                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59241             tpls.hsplit.disableFormats = true;
59242         }
59243         tpls.hsplit.compile();
59244
59245         if(!tpls.body){
59246             tpls.body = new Roo.Template(
59247                '<table border="0" cellspacing="0" cellpadding="0">',
59248                "<tbody>{rows}</tbody>",
59249                "</table>"
59250             );
59251             tpls.body.disableFormats = true;
59252         }
59253         tpls.body.compile();
59254
59255         if(!tpls.row){
59256             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59257             tpls.row.disableFormats = true;
59258         }
59259         tpls.row.compile();
59260
59261         if(!tpls.cell){
59262             tpls.cell = new Roo.Template(
59263                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59264                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59265                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59266                 "</td>"
59267             );
59268             tpls.cell.disableFormats = true;
59269         }
59270         tpls.cell.compile();
59271
59272         this.templates = tpls;
59273     },
59274
59275     // remap these for backwards compat
59276     onColWidthChange : function(){
59277         this.updateColumns.apply(this, arguments);
59278     },
59279     onHeaderChange : function(){
59280         this.updateHeaders.apply(this, arguments);
59281     }, 
59282     onHiddenChange : function(){
59283         this.handleHiddenChange.apply(this, arguments);
59284     },
59285     onColumnMove : function(){
59286         this.handleColumnMove.apply(this, arguments);
59287     },
59288     onColumnLock : function(){
59289         this.handleLockChange.apply(this, arguments);
59290     },
59291
59292     onDataChange : function(){
59293         this.refresh();
59294         this.updateHeaderSortState();
59295     },
59296
59297     onClear : function(){
59298         this.refresh();
59299     },
59300
59301     onUpdate : function(ds, record){
59302         this.refreshRow(record);
59303     },
59304
59305     refreshRow : function(record){
59306         var ds = this.ds, index;
59307         if(typeof record == 'number'){
59308             index = record;
59309             record = ds.getAt(index);
59310         }else{
59311             index = ds.indexOf(record);
59312         }
59313         this.insertRows(ds, index, index, true);
59314         this.onRemove(ds, record, index+1, true);
59315         this.syncRowHeights(index, index);
59316         this.layout();
59317         this.fireEvent("rowupdated", this, index, record);
59318     },
59319
59320     onAdd : function(ds, records, index){
59321         this.insertRows(ds, index, index + (records.length-1));
59322     },
59323
59324     onRemove : function(ds, record, index, isUpdate){
59325         if(isUpdate !== true){
59326             this.fireEvent("beforerowremoved", this, index, record);
59327         }
59328         var bt = this.getBodyTable(), lt = this.getLockedTable();
59329         if(bt.rows[index]){
59330             bt.firstChild.removeChild(bt.rows[index]);
59331         }
59332         if(lt.rows[index]){
59333             lt.firstChild.removeChild(lt.rows[index]);
59334         }
59335         if(isUpdate !== true){
59336             this.stripeRows(index);
59337             this.syncRowHeights(index, index);
59338             this.layout();
59339             this.fireEvent("rowremoved", this, index, record);
59340         }
59341     },
59342
59343     onLoad : function(){
59344         this.scrollToTop();
59345     },
59346
59347     /**
59348      * Scrolls the grid to the top
59349      */
59350     scrollToTop : function(){
59351         if(this.scroller){
59352             this.scroller.dom.scrollTop = 0;
59353             this.syncScroll();
59354         }
59355     },
59356
59357     /**
59358      * Gets a panel in the header of the grid that can be used for toolbars etc.
59359      * After modifying the contents of this panel a call to grid.autoSize() may be
59360      * required to register any changes in size.
59361      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59362      * @return Roo.Element
59363      */
59364     getHeaderPanel : function(doShow){
59365         if(doShow){
59366             this.headerPanel.show();
59367         }
59368         return this.headerPanel;
59369     },
59370
59371     /**
59372      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59373      * After modifying the contents of this panel a call to grid.autoSize() may be
59374      * required to register any changes in size.
59375      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59376      * @return Roo.Element
59377      */
59378     getFooterPanel : function(doShow){
59379         if(doShow){
59380             this.footerPanel.show();
59381         }
59382         return this.footerPanel;
59383     },
59384
59385     initElements : function(){
59386         var E = Roo.Element;
59387         var el = this.grid.getGridEl().dom.firstChild;
59388         var cs = el.childNodes;
59389
59390         this.el = new E(el);
59391         
59392          this.focusEl = new E(el.firstChild);
59393         this.focusEl.swallowEvent("click", true);
59394         
59395         this.headerPanel = new E(cs[1]);
59396         this.headerPanel.enableDisplayMode("block");
59397
59398         this.scroller = new E(cs[2]);
59399         this.scrollSizer = new E(this.scroller.dom.firstChild);
59400
59401         this.lockedWrap = new E(cs[3]);
59402         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59403         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59404
59405         this.mainWrap = new E(cs[4]);
59406         this.mainHd = new E(this.mainWrap.dom.firstChild);
59407         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59408
59409         this.footerPanel = new E(cs[5]);
59410         this.footerPanel.enableDisplayMode("block");
59411
59412         this.resizeProxy = new E(cs[6]);
59413
59414         this.headerSelector = String.format(
59415            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59416            this.lockedHd.id, this.mainHd.id
59417         );
59418
59419         this.splitterSelector = String.format(
59420            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59421            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59422         );
59423     },
59424     idToCssName : function(s)
59425     {
59426         return s.replace(/[^a-z0-9]+/ig, '-');
59427     },
59428
59429     getHeaderCell : function(index){
59430         return Roo.DomQuery.select(this.headerSelector)[index];
59431     },
59432
59433     getHeaderCellMeasure : function(index){
59434         return this.getHeaderCell(index).firstChild;
59435     },
59436
59437     getHeaderCellText : function(index){
59438         return this.getHeaderCell(index).firstChild.firstChild;
59439     },
59440
59441     getLockedTable : function(){
59442         return this.lockedBody.dom.firstChild;
59443     },
59444
59445     getBodyTable : function(){
59446         return this.mainBody.dom.firstChild;
59447     },
59448
59449     getLockedRow : function(index){
59450         return this.getLockedTable().rows[index];
59451     },
59452
59453     getRow : function(index){
59454         return this.getBodyTable().rows[index];
59455     },
59456
59457     getRowComposite : function(index){
59458         if(!this.rowEl){
59459             this.rowEl = new Roo.CompositeElementLite();
59460         }
59461         var els = [], lrow, mrow;
59462         if(lrow = this.getLockedRow(index)){
59463             els.push(lrow);
59464         }
59465         if(mrow = this.getRow(index)){
59466             els.push(mrow);
59467         }
59468         this.rowEl.elements = els;
59469         return this.rowEl;
59470     },
59471     /**
59472      * Gets the 'td' of the cell
59473      * 
59474      * @param {Integer} rowIndex row to select
59475      * @param {Integer} colIndex column to select
59476      * 
59477      * @return {Object} 
59478      */
59479     getCell : function(rowIndex, colIndex){
59480         var locked = this.cm.getLockedCount();
59481         var source;
59482         if(colIndex < locked){
59483             source = this.lockedBody.dom.firstChild;
59484         }else{
59485             source = this.mainBody.dom.firstChild;
59486             colIndex -= locked;
59487         }
59488         return source.rows[rowIndex].childNodes[colIndex];
59489     },
59490
59491     getCellText : function(rowIndex, colIndex){
59492         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59493     },
59494
59495     getCellBox : function(cell){
59496         var b = this.fly(cell).getBox();
59497         if(Roo.isOpera){ // opera fails to report the Y
59498             b.y = cell.offsetTop + this.mainBody.getY();
59499         }
59500         return b;
59501     },
59502
59503     getCellIndex : function(cell){
59504         var id = String(cell.className).match(this.cellRE);
59505         if(id){
59506             return parseInt(id[1], 10);
59507         }
59508         return 0;
59509     },
59510
59511     findHeaderIndex : function(n){
59512         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59513         return r ? this.getCellIndex(r) : false;
59514     },
59515
59516     findHeaderCell : function(n){
59517         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59518         return r ? r : false;
59519     },
59520
59521     findRowIndex : function(n){
59522         if(!n){
59523             return false;
59524         }
59525         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59526         return r ? r.rowIndex : false;
59527     },
59528
59529     findCellIndex : function(node){
59530         var stop = this.el.dom;
59531         while(node && node != stop){
59532             if(this.findRE.test(node.className)){
59533                 return this.getCellIndex(node);
59534             }
59535             node = node.parentNode;
59536         }
59537         return false;
59538     },
59539
59540     getColumnId : function(index){
59541         return this.cm.getColumnId(index);
59542     },
59543
59544     getSplitters : function()
59545     {
59546         if(this.splitterSelector){
59547            return Roo.DomQuery.select(this.splitterSelector);
59548         }else{
59549             return null;
59550       }
59551     },
59552
59553     getSplitter : function(index){
59554         return this.getSplitters()[index];
59555     },
59556
59557     onRowOver : function(e, t){
59558         var row;
59559         if((row = this.findRowIndex(t)) !== false){
59560             this.getRowComposite(row).addClass("x-grid-row-over");
59561         }
59562     },
59563
59564     onRowOut : function(e, t){
59565         var row;
59566         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59567             this.getRowComposite(row).removeClass("x-grid-row-over");
59568         }
59569     },
59570
59571     renderHeaders : function(){
59572         var cm = this.cm;
59573         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59574         var cb = [], lb = [], sb = [], lsb = [], p = {};
59575         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59576             p.cellId = "x-grid-hd-0-" + i;
59577             p.splitId = "x-grid-csplit-0-" + i;
59578             p.id = cm.getColumnId(i);
59579             p.value = cm.getColumnHeader(i) || "";
59580             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59581             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59582             if(!cm.isLocked(i)){
59583                 cb[cb.length] = ct.apply(p);
59584                 sb[sb.length] = st.apply(p);
59585             }else{
59586                 lb[lb.length] = ct.apply(p);
59587                 lsb[lsb.length] = st.apply(p);
59588             }
59589         }
59590         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59591                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59592     },
59593
59594     updateHeaders : function(){
59595         var html = this.renderHeaders();
59596         this.lockedHd.update(html[0]);
59597         this.mainHd.update(html[1]);
59598     },
59599
59600     /**
59601      * Focuses the specified row.
59602      * @param {Number} row The row index
59603      */
59604     focusRow : function(row)
59605     {
59606         //Roo.log('GridView.focusRow');
59607         var x = this.scroller.dom.scrollLeft;
59608         this.focusCell(row, 0, false);
59609         this.scroller.dom.scrollLeft = x;
59610     },
59611
59612     /**
59613      * Focuses the specified cell.
59614      * @param {Number} row The row index
59615      * @param {Number} col The column index
59616      * @param {Boolean} hscroll false to disable horizontal scrolling
59617      */
59618     focusCell : function(row, col, hscroll)
59619     {
59620         //Roo.log('GridView.focusCell');
59621         var el = this.ensureVisible(row, col, hscroll);
59622         this.focusEl.alignTo(el, "tl-tl");
59623         if(Roo.isGecko){
59624             this.focusEl.focus();
59625         }else{
59626             this.focusEl.focus.defer(1, this.focusEl);
59627         }
59628     },
59629
59630     /**
59631      * Scrolls the specified cell into view
59632      * @param {Number} row The row index
59633      * @param {Number} col The column index
59634      * @param {Boolean} hscroll false to disable horizontal scrolling
59635      */
59636     ensureVisible : function(row, col, hscroll)
59637     {
59638         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59639         //return null; //disable for testing.
59640         if(typeof row != "number"){
59641             row = row.rowIndex;
59642         }
59643         if(row < 0 && row >= this.ds.getCount()){
59644             return  null;
59645         }
59646         col = (col !== undefined ? col : 0);
59647         var cm = this.grid.colModel;
59648         while(cm.isHidden(col)){
59649             col++;
59650         }
59651
59652         var el = this.getCell(row, col);
59653         if(!el){
59654             return null;
59655         }
59656         var c = this.scroller.dom;
59657
59658         var ctop = parseInt(el.offsetTop, 10);
59659         var cleft = parseInt(el.offsetLeft, 10);
59660         var cbot = ctop + el.offsetHeight;
59661         var cright = cleft + el.offsetWidth;
59662         
59663         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59664         var stop = parseInt(c.scrollTop, 10);
59665         var sleft = parseInt(c.scrollLeft, 10);
59666         var sbot = stop + ch;
59667         var sright = sleft + c.clientWidth;
59668         /*
59669         Roo.log('GridView.ensureVisible:' +
59670                 ' ctop:' + ctop +
59671                 ' c.clientHeight:' + c.clientHeight +
59672                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59673                 ' stop:' + stop +
59674                 ' cbot:' + cbot +
59675                 ' sbot:' + sbot +
59676                 ' ch:' + ch  
59677                 );
59678         */
59679         if(ctop < stop){
59680             c.scrollTop = ctop;
59681             //Roo.log("set scrolltop to ctop DISABLE?");
59682         }else if(cbot > sbot){
59683             //Roo.log("set scrolltop to cbot-ch");
59684             c.scrollTop = cbot-ch;
59685         }
59686         
59687         if(hscroll !== false){
59688             if(cleft < sleft){
59689                 c.scrollLeft = cleft;
59690             }else if(cright > sright){
59691                 c.scrollLeft = cright-c.clientWidth;
59692             }
59693         }
59694          
59695         return el;
59696     },
59697
59698     updateColumns : function(){
59699         this.grid.stopEditing();
59700         var cm = this.grid.colModel, colIds = this.getColumnIds();
59701         //var totalWidth = cm.getTotalWidth();
59702         var pos = 0;
59703         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59704             //if(cm.isHidden(i)) continue;
59705             var w = cm.getColumnWidth(i);
59706             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59707             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59708         }
59709         this.updateSplitters();
59710     },
59711
59712     generateRules : function(cm){
59713         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59714         Roo.util.CSS.removeStyleSheet(rulesId);
59715         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59716             var cid = cm.getColumnId(i);
59717             var align = '';
59718             if(cm.config[i].align){
59719                 align = 'text-align:'+cm.config[i].align+';';
59720             }
59721             var hidden = '';
59722             if(cm.isHidden(i)){
59723                 hidden = 'display:none;';
59724             }
59725             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59726             ruleBuf.push(
59727                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59728                     this.hdSelector, cid, " {\n", align, width, "}\n",
59729                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59730                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59731         }
59732         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59733     },
59734
59735     updateSplitters : function(){
59736         var cm = this.cm, s = this.getSplitters();
59737         if(s){ // splitters not created yet
59738             var pos = 0, locked = true;
59739             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59740                 if(cm.isHidden(i)) {
59741                     continue;
59742                 }
59743                 var w = cm.getColumnWidth(i); // make sure it's a number
59744                 if(!cm.isLocked(i) && locked){
59745                     pos = 0;
59746                     locked = false;
59747                 }
59748                 pos += w;
59749                 s[i].style.left = (pos-this.splitOffset) + "px";
59750             }
59751         }
59752     },
59753
59754     handleHiddenChange : function(colModel, colIndex, hidden){
59755         if(hidden){
59756             this.hideColumn(colIndex);
59757         }else{
59758             this.unhideColumn(colIndex);
59759         }
59760     },
59761
59762     hideColumn : function(colIndex){
59763         var cid = this.getColumnId(colIndex);
59764         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59765         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59766         if(Roo.isSafari){
59767             this.updateHeaders();
59768         }
59769         this.updateSplitters();
59770         this.layout();
59771     },
59772
59773     unhideColumn : function(colIndex){
59774         var cid = this.getColumnId(colIndex);
59775         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59776         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59777
59778         if(Roo.isSafari){
59779             this.updateHeaders();
59780         }
59781         this.updateSplitters();
59782         this.layout();
59783     },
59784
59785     insertRows : function(dm, firstRow, lastRow, isUpdate){
59786         if(firstRow == 0 && lastRow == dm.getCount()-1){
59787             this.refresh();
59788         }else{
59789             if(!isUpdate){
59790                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59791             }
59792             var s = this.getScrollState();
59793             var markup = this.renderRows(firstRow, lastRow);
59794             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59795             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59796             this.restoreScroll(s);
59797             if(!isUpdate){
59798                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59799                 this.syncRowHeights(firstRow, lastRow);
59800                 this.stripeRows(firstRow);
59801                 this.layout();
59802             }
59803         }
59804     },
59805
59806     bufferRows : function(markup, target, index){
59807         var before = null, trows = target.rows, tbody = target.tBodies[0];
59808         if(index < trows.length){
59809             before = trows[index];
59810         }
59811         var b = document.createElement("div");
59812         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59813         var rows = b.firstChild.rows;
59814         for(var i = 0, len = rows.length; i < len; i++){
59815             if(before){
59816                 tbody.insertBefore(rows[0], before);
59817             }else{
59818                 tbody.appendChild(rows[0]);
59819             }
59820         }
59821         b.innerHTML = "";
59822         b = null;
59823     },
59824
59825     deleteRows : function(dm, firstRow, lastRow){
59826         if(dm.getRowCount()<1){
59827             this.fireEvent("beforerefresh", this);
59828             this.mainBody.update("");
59829             this.lockedBody.update("");
59830             this.fireEvent("refresh", this);
59831         }else{
59832             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59833             var bt = this.getBodyTable();
59834             var tbody = bt.firstChild;
59835             var rows = bt.rows;
59836             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59837                 tbody.removeChild(rows[firstRow]);
59838             }
59839             this.stripeRows(firstRow);
59840             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59841         }
59842     },
59843
59844     updateRows : function(dataSource, firstRow, lastRow){
59845         var s = this.getScrollState();
59846         this.refresh();
59847         this.restoreScroll(s);
59848     },
59849
59850     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59851         if(!noRefresh){
59852            this.refresh();
59853         }
59854         this.updateHeaderSortState();
59855     },
59856
59857     getScrollState : function(){
59858         
59859         var sb = this.scroller.dom;
59860         return {left: sb.scrollLeft, top: sb.scrollTop};
59861     },
59862
59863     stripeRows : function(startRow){
59864         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59865             return;
59866         }
59867         startRow = startRow || 0;
59868         var rows = this.getBodyTable().rows;
59869         var lrows = this.getLockedTable().rows;
59870         var cls = ' x-grid-row-alt ';
59871         for(var i = startRow, len = rows.length; i < len; i++){
59872             var row = rows[i], lrow = lrows[i];
59873             var isAlt = ((i+1) % 2 == 0);
59874             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59875             if(isAlt == hasAlt){
59876                 continue;
59877             }
59878             if(isAlt){
59879                 row.className += " x-grid-row-alt";
59880             }else{
59881                 row.className = row.className.replace("x-grid-row-alt", "");
59882             }
59883             if(lrow){
59884                 lrow.className = row.className;
59885             }
59886         }
59887     },
59888
59889     restoreScroll : function(state){
59890         //Roo.log('GridView.restoreScroll');
59891         var sb = this.scroller.dom;
59892         sb.scrollLeft = state.left;
59893         sb.scrollTop = state.top;
59894         this.syncScroll();
59895     },
59896
59897     syncScroll : function(){
59898         //Roo.log('GridView.syncScroll');
59899         var sb = this.scroller.dom;
59900         var sh = this.mainHd.dom;
59901         var bs = this.mainBody.dom;
59902         var lv = this.lockedBody.dom;
59903         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59904         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59905     },
59906
59907     handleScroll : function(e){
59908         this.syncScroll();
59909         var sb = this.scroller.dom;
59910         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59911         e.stopEvent();
59912     },
59913
59914     handleWheel : function(e){
59915         var d = e.getWheelDelta();
59916         this.scroller.dom.scrollTop -= d*22;
59917         // set this here to prevent jumpy scrolling on large tables
59918         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59919         e.stopEvent();
59920     },
59921
59922     renderRows : function(startRow, endRow){
59923         // pull in all the crap needed to render rows
59924         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59925         var colCount = cm.getColumnCount();
59926
59927         if(ds.getCount() < 1){
59928             return ["", ""];
59929         }
59930
59931         // build a map for all the columns
59932         var cs = [];
59933         for(var i = 0; i < colCount; i++){
59934             var name = cm.getDataIndex(i);
59935             cs[i] = {
59936                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59937                 renderer : cm.getRenderer(i),
59938                 id : cm.getColumnId(i),
59939                 locked : cm.isLocked(i),
59940                 has_editor : cm.isCellEditable(i)
59941             };
59942         }
59943
59944         startRow = startRow || 0;
59945         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59946
59947         // records to render
59948         var rs = ds.getRange(startRow, endRow);
59949
59950         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59951     },
59952
59953     // As much as I hate to duplicate code, this was branched because FireFox really hates
59954     // [].join("") on strings. The performance difference was substantial enough to
59955     // branch this function
59956     doRender : Roo.isGecko ?
59957             function(cs, rs, ds, startRow, colCount, stripe){
59958                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59959                 // buffers
59960                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59961                 
59962                 var hasListener = this.grid.hasListener('rowclass');
59963                 var rowcfg = {};
59964                 for(var j = 0, len = rs.length; j < len; j++){
59965                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59966                     for(var i = 0; i < colCount; i++){
59967                         c = cs[i];
59968                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59969                         p.id = c.id;
59970                         p.css = p.attr = "";
59971                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59972                         if(p.value == undefined || p.value === "") {
59973                             p.value = "&#160;";
59974                         }
59975                         if(c.has_editor){
59976                             p.css += ' x-grid-editable-cell';
59977                         }
59978                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59979                             p.css +=  ' x-grid-dirty-cell';
59980                         }
59981                         var markup = ct.apply(p);
59982                         if(!c.locked){
59983                             cb+= markup;
59984                         }else{
59985                             lcb+= markup;
59986                         }
59987                     }
59988                     var alt = [];
59989                     if(stripe && ((rowIndex+1) % 2 == 0)){
59990                         alt.push("x-grid-row-alt")
59991                     }
59992                     if(r.dirty){
59993                         alt.push(  " x-grid-dirty-row");
59994                     }
59995                     rp.cells = lcb;
59996                     if(this.getRowClass){
59997                         alt.push(this.getRowClass(r, rowIndex));
59998                     }
59999                     if (hasListener) {
60000                         rowcfg = {
60001                              
60002                             record: r,
60003                             rowIndex : rowIndex,
60004                             rowClass : ''
60005                         };
60006                         this.grid.fireEvent('rowclass', this, rowcfg);
60007                         alt.push(rowcfg.rowClass);
60008                     }
60009                     rp.alt = alt.join(" ");
60010                     lbuf+= rt.apply(rp);
60011                     rp.cells = cb;
60012                     buf+=  rt.apply(rp);
60013                 }
60014                 return [lbuf, buf];
60015             } :
60016             function(cs, rs, ds, startRow, colCount, stripe){
60017                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60018                 // buffers
60019                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60020                 var hasListener = this.grid.hasListener('rowclass');
60021  
60022                 var rowcfg = {};
60023                 for(var j = 0, len = rs.length; j < len; j++){
60024                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60025                     for(var i = 0; i < colCount; i++){
60026                         c = cs[i];
60027                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60028                         p.id = c.id;
60029                         p.css = p.attr = "";
60030                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60031                         if(p.value == undefined || p.value === "") {
60032                             p.value = "&#160;";
60033                         }
60034                         //Roo.log(c);
60035                          if(c.has_editor){
60036                             p.css += ' x-grid-editable-cell';
60037                         }
60038                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60039                             p.css += ' x-grid-dirty-cell' 
60040                         }
60041                         
60042                         var markup = ct.apply(p);
60043                         if(!c.locked){
60044                             cb[cb.length] = markup;
60045                         }else{
60046                             lcb[lcb.length] = markup;
60047                         }
60048                     }
60049                     var alt = [];
60050                     if(stripe && ((rowIndex+1) % 2 == 0)){
60051                         alt.push( "x-grid-row-alt");
60052                     }
60053                     if(r.dirty){
60054                         alt.push(" x-grid-dirty-row");
60055                     }
60056                     rp.cells = lcb;
60057                     if(this.getRowClass){
60058                         alt.push( this.getRowClass(r, rowIndex));
60059                     }
60060                     if (hasListener) {
60061                         rowcfg = {
60062                              
60063                             record: r,
60064                             rowIndex : rowIndex,
60065                             rowClass : ''
60066                         };
60067                         this.grid.fireEvent('rowclass', this, rowcfg);
60068                         alt.push(rowcfg.rowClass);
60069                     }
60070                     
60071                     rp.alt = alt.join(" ");
60072                     rp.cells = lcb.join("");
60073                     lbuf[lbuf.length] = rt.apply(rp);
60074                     rp.cells = cb.join("");
60075                     buf[buf.length] =  rt.apply(rp);
60076                 }
60077                 return [lbuf.join(""), buf.join("")];
60078             },
60079
60080     renderBody : function(){
60081         var markup = this.renderRows();
60082         var bt = this.templates.body;
60083         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60084     },
60085
60086     /**
60087      * Refreshes the grid
60088      * @param {Boolean} headersToo
60089      */
60090     refresh : function(headersToo){
60091         this.fireEvent("beforerefresh", this);
60092         this.grid.stopEditing();
60093         var result = this.renderBody();
60094         this.lockedBody.update(result[0]);
60095         this.mainBody.update(result[1]);
60096         if(headersToo === true){
60097             this.updateHeaders();
60098             this.updateColumns();
60099             this.updateSplitters();
60100             this.updateHeaderSortState();
60101         }
60102         this.syncRowHeights();
60103         this.layout();
60104         this.fireEvent("refresh", this);
60105     },
60106
60107     handleColumnMove : function(cm, oldIndex, newIndex){
60108         this.indexMap = null;
60109         var s = this.getScrollState();
60110         this.refresh(true);
60111         this.restoreScroll(s);
60112         this.afterMove(newIndex);
60113     },
60114
60115     afterMove : function(colIndex){
60116         if(this.enableMoveAnim && Roo.enableFx){
60117             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60118         }
60119         // if multisort - fix sortOrder, and reload..
60120         if (this.grid.dataSource.multiSort) {
60121             // the we can call sort again..
60122             var dm = this.grid.dataSource;
60123             var cm = this.grid.colModel;
60124             var so = [];
60125             for(var i = 0; i < cm.config.length; i++ ) {
60126                 
60127                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60128                     continue; // dont' bother, it's not in sort list or being set.
60129                 }
60130                 
60131                 so.push(cm.config[i].dataIndex);
60132             };
60133             dm.sortOrder = so;
60134             dm.load(dm.lastOptions);
60135             
60136             
60137         }
60138         
60139     },
60140
60141     updateCell : function(dm, rowIndex, dataIndex){
60142         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60143         if(typeof colIndex == "undefined"){ // not present in grid
60144             return;
60145         }
60146         var cm = this.grid.colModel;
60147         var cell = this.getCell(rowIndex, colIndex);
60148         var cellText = this.getCellText(rowIndex, colIndex);
60149
60150         var p = {
60151             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60152             id : cm.getColumnId(colIndex),
60153             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60154         };
60155         var renderer = cm.getRenderer(colIndex);
60156         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60157         if(typeof val == "undefined" || val === "") {
60158             val = "&#160;";
60159         }
60160         cellText.innerHTML = val;
60161         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60162         this.syncRowHeights(rowIndex, rowIndex);
60163     },
60164
60165     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60166         var maxWidth = 0;
60167         if(this.grid.autoSizeHeaders){
60168             var h = this.getHeaderCellMeasure(colIndex);
60169             maxWidth = Math.max(maxWidth, h.scrollWidth);
60170         }
60171         var tb, index;
60172         if(this.cm.isLocked(colIndex)){
60173             tb = this.getLockedTable();
60174             index = colIndex;
60175         }else{
60176             tb = this.getBodyTable();
60177             index = colIndex - this.cm.getLockedCount();
60178         }
60179         if(tb && tb.rows){
60180             var rows = tb.rows;
60181             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60182             for(var i = 0; i < stopIndex; i++){
60183                 var cell = rows[i].childNodes[index].firstChild;
60184                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60185             }
60186         }
60187         return maxWidth + /*margin for error in IE*/ 5;
60188     },
60189     /**
60190      * Autofit a column to its content.
60191      * @param {Number} colIndex
60192      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60193      */
60194      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60195          if(this.cm.isHidden(colIndex)){
60196              return; // can't calc a hidden column
60197          }
60198         if(forceMinSize){
60199             var cid = this.cm.getColumnId(colIndex);
60200             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60201            if(this.grid.autoSizeHeaders){
60202                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60203            }
60204         }
60205         var newWidth = this.calcColumnWidth(colIndex);
60206         this.cm.setColumnWidth(colIndex,
60207             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60208         if(!suppressEvent){
60209             this.grid.fireEvent("columnresize", colIndex, newWidth);
60210         }
60211     },
60212
60213     /**
60214      * Autofits all columns to their content and then expands to fit any extra space in the grid
60215      */
60216      autoSizeColumns : function(){
60217         var cm = this.grid.colModel;
60218         var colCount = cm.getColumnCount();
60219         for(var i = 0; i < colCount; i++){
60220             this.autoSizeColumn(i, true, true);
60221         }
60222         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60223             this.fitColumns();
60224         }else{
60225             this.updateColumns();
60226             this.layout();
60227         }
60228     },
60229
60230     /**
60231      * Autofits all columns to the grid's width proportionate with their current size
60232      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60233      */
60234     fitColumns : function(reserveScrollSpace){
60235         var cm = this.grid.colModel;
60236         var colCount = cm.getColumnCount();
60237         var cols = [];
60238         var width = 0;
60239         var i, w;
60240         for (i = 0; i < colCount; i++){
60241             if(!cm.isHidden(i) && !cm.isFixed(i)){
60242                 w = cm.getColumnWidth(i);
60243                 cols.push(i);
60244                 cols.push(w);
60245                 width += w;
60246             }
60247         }
60248         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60249         if(reserveScrollSpace){
60250             avail -= 17;
60251         }
60252         var frac = (avail - cm.getTotalWidth())/width;
60253         while (cols.length){
60254             w = cols.pop();
60255             i = cols.pop();
60256             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60257         }
60258         this.updateColumns();
60259         this.layout();
60260     },
60261
60262     onRowSelect : function(rowIndex){
60263         var row = this.getRowComposite(rowIndex);
60264         row.addClass("x-grid-row-selected");
60265     },
60266
60267     onRowDeselect : function(rowIndex){
60268         var row = this.getRowComposite(rowIndex);
60269         row.removeClass("x-grid-row-selected");
60270     },
60271
60272     onCellSelect : function(row, col){
60273         var cell = this.getCell(row, col);
60274         if(cell){
60275             Roo.fly(cell).addClass("x-grid-cell-selected");
60276         }
60277     },
60278
60279     onCellDeselect : function(row, col){
60280         var cell = this.getCell(row, col);
60281         if(cell){
60282             Roo.fly(cell).removeClass("x-grid-cell-selected");
60283         }
60284     },
60285
60286     updateHeaderSortState : function(){
60287         
60288         // sort state can be single { field: xxx, direction : yyy}
60289         // or   { xxx=>ASC , yyy : DESC ..... }
60290         
60291         var mstate = {};
60292         if (!this.ds.multiSort) { 
60293             var state = this.ds.getSortState();
60294             if(!state){
60295                 return;
60296             }
60297             mstate[state.field] = state.direction;
60298             // FIXME... - this is not used here.. but might be elsewhere..
60299             this.sortState = state;
60300             
60301         } else {
60302             mstate = this.ds.sortToggle;
60303         }
60304         //remove existing sort classes..
60305         
60306         var sc = this.sortClasses;
60307         var hds = this.el.select(this.headerSelector).removeClass(sc);
60308         
60309         for(var f in mstate) {
60310         
60311             var sortColumn = this.cm.findColumnIndex(f);
60312             
60313             if(sortColumn != -1){
60314                 var sortDir = mstate[f];        
60315                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60316             }
60317         }
60318         
60319          
60320         
60321     },
60322
60323
60324     handleHeaderClick : function(g, index,e){
60325         
60326         Roo.log("header click");
60327         
60328         if (Roo.isTouch) {
60329             // touch events on header are handled by context
60330             this.handleHdCtx(g,index,e);
60331             return;
60332         }
60333         
60334         
60335         if(this.headersDisabled){
60336             return;
60337         }
60338         var dm = g.dataSource, cm = g.colModel;
60339         if(!cm.isSortable(index)){
60340             return;
60341         }
60342         g.stopEditing();
60343         
60344         if (dm.multiSort) {
60345             // update the sortOrder
60346             var so = [];
60347             for(var i = 0; i < cm.config.length; i++ ) {
60348                 
60349                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60350                     continue; // dont' bother, it's not in sort list or being set.
60351                 }
60352                 
60353                 so.push(cm.config[i].dataIndex);
60354             };
60355             dm.sortOrder = so;
60356         }
60357         
60358         
60359         dm.sort(cm.getDataIndex(index));
60360     },
60361
60362
60363     destroy : function(){
60364         if(this.colMenu){
60365             this.colMenu.removeAll();
60366             Roo.menu.MenuMgr.unregister(this.colMenu);
60367             this.colMenu.getEl().remove();
60368             delete this.colMenu;
60369         }
60370         if(this.hmenu){
60371             this.hmenu.removeAll();
60372             Roo.menu.MenuMgr.unregister(this.hmenu);
60373             this.hmenu.getEl().remove();
60374             delete this.hmenu;
60375         }
60376         if(this.grid.enableColumnMove){
60377             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60378             if(dds){
60379                 for(var dd in dds){
60380                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60381                         var elid = dds[dd].dragElId;
60382                         dds[dd].unreg();
60383                         Roo.get(elid).remove();
60384                     } else if(dds[dd].config.isTarget){
60385                         dds[dd].proxyTop.remove();
60386                         dds[dd].proxyBottom.remove();
60387                         dds[dd].unreg();
60388                     }
60389                     if(Roo.dd.DDM.locationCache[dd]){
60390                         delete Roo.dd.DDM.locationCache[dd];
60391                     }
60392                 }
60393                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60394             }
60395         }
60396         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60397         this.bind(null, null);
60398         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60399     },
60400
60401     handleLockChange : function(){
60402         this.refresh(true);
60403     },
60404
60405     onDenyColumnLock : function(){
60406
60407     },
60408
60409     onDenyColumnHide : function(){
60410
60411     },
60412
60413     handleHdMenuClick : function(item){
60414         var index = this.hdCtxIndex;
60415         var cm = this.cm, ds = this.ds;
60416         switch(item.id){
60417             case "asc":
60418                 ds.sort(cm.getDataIndex(index), "ASC");
60419                 break;
60420             case "desc":
60421                 ds.sort(cm.getDataIndex(index), "DESC");
60422                 break;
60423             case "lock":
60424                 var lc = cm.getLockedCount();
60425                 if(cm.getColumnCount(true) <= lc+1){
60426                     this.onDenyColumnLock();
60427                     return;
60428                 }
60429                 if(lc != index){
60430                     cm.setLocked(index, true, true);
60431                     cm.moveColumn(index, lc);
60432                     this.grid.fireEvent("columnmove", index, lc);
60433                 }else{
60434                     cm.setLocked(index, true);
60435                 }
60436             break;
60437             case "unlock":
60438                 var lc = cm.getLockedCount();
60439                 if((lc-1) != index){
60440                     cm.setLocked(index, false, true);
60441                     cm.moveColumn(index, lc-1);
60442                     this.grid.fireEvent("columnmove", index, lc-1);
60443                 }else{
60444                     cm.setLocked(index, false);
60445                 }
60446             break;
60447             case 'wider': // used to expand cols on touch..
60448             case 'narrow':
60449                 var cw = cm.getColumnWidth(index);
60450                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60451                 cw = Math.max(0, cw);
60452                 cw = Math.min(cw,4000);
60453                 cm.setColumnWidth(index, cw);
60454                 break;
60455                 
60456             default:
60457                 index = cm.getIndexById(item.id.substr(4));
60458                 if(index != -1){
60459                     if(item.checked && cm.getColumnCount(true) <= 1){
60460                         this.onDenyColumnHide();
60461                         return false;
60462                     }
60463                     cm.setHidden(index, item.checked);
60464                 }
60465         }
60466         return true;
60467     },
60468
60469     beforeColMenuShow : function(){
60470         var cm = this.cm,  colCount = cm.getColumnCount();
60471         this.colMenu.removeAll();
60472         
60473         var items = [];
60474         for(var i = 0; i < colCount; i++){
60475             items.push({
60476                 id: "col-"+cm.getColumnId(i),
60477                 text: cm.getColumnHeader(i),
60478                 checked: !cm.isHidden(i),
60479                 hideOnClick:false
60480             });
60481         }
60482         
60483         if (this.grid.sortColMenu) {
60484             items.sort(function(a,b) {
60485                 if (a.text == b.text) {
60486                     return 0;
60487                 }
60488                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60489             });
60490         }
60491         
60492         for(var i = 0; i < colCount; i++){
60493             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60494         }
60495     },
60496
60497     handleHdCtx : function(g, index, e){
60498         e.stopEvent();
60499         var hd = this.getHeaderCell(index);
60500         this.hdCtxIndex = index;
60501         var ms = this.hmenu.items, cm = this.cm;
60502         ms.get("asc").setDisabled(!cm.isSortable(index));
60503         ms.get("desc").setDisabled(!cm.isSortable(index));
60504         if(this.grid.enableColLock !== false){
60505             ms.get("lock").setDisabled(cm.isLocked(index));
60506             ms.get("unlock").setDisabled(!cm.isLocked(index));
60507         }
60508         this.hmenu.show(hd, "tl-bl");
60509     },
60510
60511     handleHdOver : function(e){
60512         var hd = this.findHeaderCell(e.getTarget());
60513         if(hd && !this.headersDisabled){
60514             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60515                this.fly(hd).addClass("x-grid-hd-over");
60516             }
60517         }
60518     },
60519
60520     handleHdOut : function(e){
60521         var hd = this.findHeaderCell(e.getTarget());
60522         if(hd){
60523             this.fly(hd).removeClass("x-grid-hd-over");
60524         }
60525     },
60526
60527     handleSplitDblClick : function(e, t){
60528         var i = this.getCellIndex(t);
60529         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60530             this.autoSizeColumn(i, true);
60531             this.layout();
60532         }
60533     },
60534
60535     render : function(){
60536
60537         var cm = this.cm;
60538         var colCount = cm.getColumnCount();
60539
60540         if(this.grid.monitorWindowResize === true){
60541             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60542         }
60543         var header = this.renderHeaders();
60544         var body = this.templates.body.apply({rows:""});
60545         var html = this.templates.master.apply({
60546             lockedBody: body,
60547             body: body,
60548             lockedHeader: header[0],
60549             header: header[1]
60550         });
60551
60552         //this.updateColumns();
60553
60554         this.grid.getGridEl().dom.innerHTML = html;
60555
60556         this.initElements();
60557         
60558         // a kludge to fix the random scolling effect in webkit
60559         this.el.on("scroll", function() {
60560             this.el.dom.scrollTop=0; // hopefully not recursive..
60561         },this);
60562
60563         this.scroller.on("scroll", this.handleScroll, this);
60564         this.lockedBody.on("mousewheel", this.handleWheel, this);
60565         this.mainBody.on("mousewheel", this.handleWheel, this);
60566
60567         this.mainHd.on("mouseover", this.handleHdOver, this);
60568         this.mainHd.on("mouseout", this.handleHdOut, this);
60569         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60570                 {delegate: "."+this.splitClass});
60571
60572         this.lockedHd.on("mouseover", this.handleHdOver, this);
60573         this.lockedHd.on("mouseout", this.handleHdOut, this);
60574         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60575                 {delegate: "."+this.splitClass});
60576
60577         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60578             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60579         }
60580
60581         this.updateSplitters();
60582
60583         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60584             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60585             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60586         }
60587
60588         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60589             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60590             this.hmenu.add(
60591                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60592                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60593             );
60594             if(this.grid.enableColLock !== false){
60595                 this.hmenu.add('-',
60596                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60597                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60598                 );
60599             }
60600             if (Roo.isTouch) {
60601                  this.hmenu.add('-',
60602                     {id:"wider", text: this.columnsWiderText},
60603                     {id:"narrow", text: this.columnsNarrowText }
60604                 );
60605                 
60606                  
60607             }
60608             
60609             if(this.grid.enableColumnHide !== false){
60610
60611                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60612                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60613                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60614
60615                 this.hmenu.add('-',
60616                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60617                 );
60618             }
60619             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60620
60621             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60622         }
60623
60624         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60625             this.dd = new Roo.grid.GridDragZone(this.grid, {
60626                 ddGroup : this.grid.ddGroup || 'GridDD'
60627             });
60628             
60629         }
60630
60631         /*
60632         for(var i = 0; i < colCount; i++){
60633             if(cm.isHidden(i)){
60634                 this.hideColumn(i);
60635             }
60636             if(cm.config[i].align){
60637                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60638                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60639             }
60640         }*/
60641         
60642         this.updateHeaderSortState();
60643
60644         this.beforeInitialResize();
60645         this.layout(true);
60646
60647         // two part rendering gives faster view to the user
60648         this.renderPhase2.defer(1, this);
60649     },
60650
60651     renderPhase2 : function(){
60652         // render the rows now
60653         this.refresh();
60654         if(this.grid.autoSizeColumns){
60655             this.autoSizeColumns();
60656         }
60657     },
60658
60659     beforeInitialResize : function(){
60660
60661     },
60662
60663     onColumnSplitterMoved : function(i, w){
60664         this.userResized = true;
60665         var cm = this.grid.colModel;
60666         cm.setColumnWidth(i, w, true);
60667         var cid = cm.getColumnId(i);
60668         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60669         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60670         this.updateSplitters();
60671         this.layout();
60672         this.grid.fireEvent("columnresize", i, w);
60673     },
60674
60675     syncRowHeights : function(startIndex, endIndex){
60676         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60677             startIndex = startIndex || 0;
60678             var mrows = this.getBodyTable().rows;
60679             var lrows = this.getLockedTable().rows;
60680             var len = mrows.length-1;
60681             endIndex = Math.min(endIndex || len, len);
60682             for(var i = startIndex; i <= endIndex; i++){
60683                 var m = mrows[i], l = lrows[i];
60684                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60685                 m.style.height = l.style.height = h + "px";
60686             }
60687         }
60688     },
60689
60690     layout : function(initialRender, is2ndPass)
60691     {
60692         var g = this.grid;
60693         var auto = g.autoHeight;
60694         var scrollOffset = 16;
60695         var c = g.getGridEl(), cm = this.cm,
60696                 expandCol = g.autoExpandColumn,
60697                 gv = this;
60698         //c.beginMeasure();
60699
60700         if(!c.dom.offsetWidth){ // display:none?
60701             if(initialRender){
60702                 this.lockedWrap.show();
60703                 this.mainWrap.show();
60704             }
60705             return;
60706         }
60707
60708         var hasLock = this.cm.isLocked(0);
60709
60710         var tbh = this.headerPanel.getHeight();
60711         var bbh = this.footerPanel.getHeight();
60712
60713         if(auto){
60714             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60715             var newHeight = ch + c.getBorderWidth("tb");
60716             if(g.maxHeight){
60717                 newHeight = Math.min(g.maxHeight, newHeight);
60718             }
60719             c.setHeight(newHeight);
60720         }
60721
60722         if(g.autoWidth){
60723             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60724         }
60725
60726         var s = this.scroller;
60727
60728         var csize = c.getSize(true);
60729
60730         this.el.setSize(csize.width, csize.height);
60731
60732         this.headerPanel.setWidth(csize.width);
60733         this.footerPanel.setWidth(csize.width);
60734
60735         var hdHeight = this.mainHd.getHeight();
60736         var vw = csize.width;
60737         var vh = csize.height - (tbh + bbh);
60738
60739         s.setSize(vw, vh);
60740
60741         var bt = this.getBodyTable();
60742         
60743         if(cm.getLockedCount() == cm.config.length){
60744             bt = this.getLockedTable();
60745         }
60746         
60747         var ltWidth = hasLock ?
60748                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60749
60750         var scrollHeight = bt.offsetHeight;
60751         var scrollWidth = ltWidth + bt.offsetWidth;
60752         var vscroll = false, hscroll = false;
60753
60754         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60755
60756         var lw = this.lockedWrap, mw = this.mainWrap;
60757         var lb = this.lockedBody, mb = this.mainBody;
60758
60759         setTimeout(function(){
60760             var t = s.dom.offsetTop;
60761             var w = s.dom.clientWidth,
60762                 h = s.dom.clientHeight;
60763
60764             lw.setTop(t);
60765             lw.setSize(ltWidth, h);
60766
60767             mw.setLeftTop(ltWidth, t);
60768             mw.setSize(w-ltWidth, h);
60769
60770             lb.setHeight(h-hdHeight);
60771             mb.setHeight(h-hdHeight);
60772
60773             if(is2ndPass !== true && !gv.userResized && expandCol){
60774                 // high speed resize without full column calculation
60775                 
60776                 var ci = cm.getIndexById(expandCol);
60777                 if (ci < 0) {
60778                     ci = cm.findColumnIndex(expandCol);
60779                 }
60780                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60781                 var expandId = cm.getColumnId(ci);
60782                 var  tw = cm.getTotalWidth(false);
60783                 var currentWidth = cm.getColumnWidth(ci);
60784                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60785                 if(currentWidth != cw){
60786                     cm.setColumnWidth(ci, cw, true);
60787                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60788                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60789                     gv.updateSplitters();
60790                     gv.layout(false, true);
60791                 }
60792             }
60793
60794             if(initialRender){
60795                 lw.show();
60796                 mw.show();
60797             }
60798             //c.endMeasure();
60799         }, 10);
60800     },
60801
60802     onWindowResize : function(){
60803         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60804             return;
60805         }
60806         this.layout();
60807     },
60808
60809     appendFooter : function(parentEl){
60810         return null;
60811     },
60812
60813     sortAscText : "Sort Ascending",
60814     sortDescText : "Sort Descending",
60815     lockText : "Lock Column",
60816     unlockText : "Unlock Column",
60817     columnsText : "Columns",
60818  
60819     columnsWiderText : "Wider",
60820     columnsNarrowText : "Thinner"
60821 });
60822
60823
60824 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60825     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60826     this.proxy.el.addClass('x-grid3-col-dd');
60827 };
60828
60829 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60830     handleMouseDown : function(e){
60831
60832     },
60833
60834     callHandleMouseDown : function(e){
60835         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60836     }
60837 });
60838 /*
60839  * Based on:
60840  * Ext JS Library 1.1.1
60841  * Copyright(c) 2006-2007, Ext JS, LLC.
60842  *
60843  * Originally Released Under LGPL - original licence link has changed is not relivant.
60844  *
60845  * Fork - LGPL
60846  * <script type="text/javascript">
60847  */
60848  /**
60849  * @extends Roo.dd.DDProxy
60850  * @class Roo.grid.SplitDragZone
60851  * Support for Column Header resizing
60852  * @constructor
60853  * @param {Object} config
60854  */
60855 // private
60856 // This is a support class used internally by the Grid components
60857 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60858     this.grid = grid;
60859     this.view = grid.getView();
60860     this.proxy = this.view.resizeProxy;
60861     Roo.grid.SplitDragZone.superclass.constructor.call(
60862         this,
60863         hd, // ID
60864         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60865         {  // CONFIG
60866             dragElId : Roo.id(this.proxy.dom),
60867             resizeFrame:false
60868         }
60869     );
60870     
60871     this.setHandleElId(Roo.id(hd));
60872     if (hd2 !== false) {
60873         this.setOuterHandleElId(Roo.id(hd2));
60874     }
60875     
60876     this.scroll = false;
60877 };
60878 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60879     fly: Roo.Element.fly,
60880
60881     b4StartDrag : function(x, y){
60882         this.view.headersDisabled = true;
60883         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60884                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60885         );
60886         this.proxy.setHeight(h);
60887         
60888         // for old system colWidth really stored the actual width?
60889         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60890         // which in reality did not work.. - it worked only for fixed sizes
60891         // for resizable we need to use actual sizes.
60892         var w = this.cm.getColumnWidth(this.cellIndex);
60893         if (!this.view.mainWrap) {
60894             // bootstrap.
60895             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60896         }
60897         
60898         
60899         
60900         // this was w-this.grid.minColumnWidth;
60901         // doesnt really make sense? - w = thie curren width or the rendered one?
60902         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60903         this.resetConstraints();
60904         this.setXConstraint(minw, 1000);
60905         this.setYConstraint(0, 0);
60906         this.minX = x - minw;
60907         this.maxX = x + 1000;
60908         this.startPos = x;
60909         if (!this.view.mainWrap) { // this is Bootstrap code..
60910             this.getDragEl().style.display='block';
60911         }
60912         
60913         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60914     },
60915
60916
60917     handleMouseDown : function(e){
60918         ev = Roo.EventObject.setEvent(e);
60919         var t = this.fly(ev.getTarget());
60920         if(t.hasClass("x-grid-split")){
60921             this.cellIndex = this.view.getCellIndex(t.dom);
60922             this.split = t.dom;
60923             this.cm = this.grid.colModel;
60924             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60925                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60926             }
60927         }
60928     },
60929
60930     endDrag : function(e){
60931         this.view.headersDisabled = false;
60932         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60933         var diff = endX - this.startPos;
60934         // 
60935         var w = this.cm.getColumnWidth(this.cellIndex);
60936         if (!this.view.mainWrap) {
60937             w = 0;
60938         }
60939         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60940     },
60941
60942     autoOffset : function(){
60943         this.setDelta(0,0);
60944     }
60945 });/*
60946  * Based on:
60947  * Ext JS Library 1.1.1
60948  * Copyright(c) 2006-2007, Ext JS, LLC.
60949  *
60950  * Originally Released Under LGPL - original licence link has changed is not relivant.
60951  *
60952  * Fork - LGPL
60953  * <script type="text/javascript">
60954  */
60955  
60956 // private
60957 // This is a support class used internally by the Grid components
60958 Roo.grid.GridDragZone = function(grid, config){
60959     this.view = grid.getView();
60960     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60961     if(this.view.lockedBody){
60962         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60963         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60964     }
60965     this.scroll = false;
60966     this.grid = grid;
60967     this.ddel = document.createElement('div');
60968     this.ddel.className = 'x-grid-dd-wrap';
60969 };
60970
60971 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60972     ddGroup : "GridDD",
60973
60974     getDragData : function(e){
60975         var t = Roo.lib.Event.getTarget(e);
60976         var rowIndex = this.view.findRowIndex(t);
60977         var sm = this.grid.selModel;
60978             
60979         //Roo.log(rowIndex);
60980         
60981         if (sm.getSelectedCell) {
60982             // cell selection..
60983             if (!sm.getSelectedCell()) {
60984                 return false;
60985             }
60986             if (rowIndex != sm.getSelectedCell()[0]) {
60987                 return false;
60988             }
60989         
60990         }
60991         if (sm.getSelections && sm.getSelections().length < 1) {
60992             return false;
60993         }
60994         
60995         
60996         // before it used to all dragging of unseleted... - now we dont do that.
60997         if(rowIndex !== false){
60998             
60999             // if editorgrid.. 
61000             
61001             
61002             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
61003                
61004             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61005               //  
61006             //}
61007             if (e.hasModifier()){
61008                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61009             }
61010             
61011             Roo.log("getDragData");
61012             
61013             return {
61014                 grid: this.grid,
61015                 ddel: this.ddel,
61016                 rowIndex: rowIndex,
61017                 selections: sm.getSelections ? sm.getSelections() : (
61018                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61019             };
61020         }
61021         return false;
61022     },
61023     
61024     
61025     onInitDrag : function(e){
61026         var data = this.dragData;
61027         this.ddel.innerHTML = this.grid.getDragDropText();
61028         this.proxy.update(this.ddel);
61029         // fire start drag?
61030     },
61031
61032     afterRepair : function(){
61033         this.dragging = false;
61034     },
61035
61036     getRepairXY : function(e, data){
61037         return false;
61038     },
61039
61040     onEndDrag : function(data, e){
61041         // fire end drag?
61042     },
61043
61044     onValidDrop : function(dd, e, id){
61045         // fire drag drop?
61046         this.hideProxy();
61047     },
61048
61049     beforeInvalidDrop : function(e, id){
61050
61051     }
61052 });/*
61053  * Based on:
61054  * Ext JS Library 1.1.1
61055  * Copyright(c) 2006-2007, Ext JS, LLC.
61056  *
61057  * Originally Released Under LGPL - original licence link has changed is not relivant.
61058  *
61059  * Fork - LGPL
61060  * <script type="text/javascript">
61061  */
61062  
61063
61064 /**
61065  * @class Roo.grid.ColumnModel
61066  * @extends Roo.util.Observable
61067  * This is the default implementation of a ColumnModel used by the Grid. It defines
61068  * the columns in the grid.
61069  * <br>Usage:<br>
61070  <pre><code>
61071  var colModel = new Roo.grid.ColumnModel([
61072         {header: "Ticker", width: 60, sortable: true, locked: true},
61073         {header: "Company Name", width: 150, sortable: true},
61074         {header: "Market Cap.", width: 100, sortable: true},
61075         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61076         {header: "Employees", width: 100, sortable: true, resizable: false}
61077  ]);
61078  </code></pre>
61079  * <p>
61080  
61081  * The config options listed for this class are options which may appear in each
61082  * individual column definition.
61083  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61084  * @constructor
61085  * @param {Object} config An Array of column config objects. See this class's
61086  * config objects for details.
61087 */
61088 Roo.grid.ColumnModel = function(config){
61089         /**
61090      * The config passed into the constructor
61091      */
61092     this.config = []; //config;
61093     this.lookup = {};
61094
61095     // if no id, create one
61096     // if the column does not have a dataIndex mapping,
61097     // map it to the order it is in the config
61098     for(var i = 0, len = config.length; i < len; i++){
61099         this.addColumn(config[i]);
61100         
61101     }
61102
61103     /**
61104      * The width of columns which have no width specified (defaults to 100)
61105      * @type Number
61106      */
61107     this.defaultWidth = 100;
61108
61109     /**
61110      * Default sortable of columns which have no sortable specified (defaults to false)
61111      * @type Boolean
61112      */
61113     this.defaultSortable = false;
61114
61115     this.addEvents({
61116         /**
61117              * @event widthchange
61118              * Fires when the width of a column changes.
61119              * @param {ColumnModel} this
61120              * @param {Number} columnIndex The column index
61121              * @param {Number} newWidth The new width
61122              */
61123             "widthchange": true,
61124         /**
61125              * @event headerchange
61126              * Fires when the text of a header changes.
61127              * @param {ColumnModel} this
61128              * @param {Number} columnIndex The column index
61129              * @param {Number} newText The new header text
61130              */
61131             "headerchange": true,
61132         /**
61133              * @event hiddenchange
61134              * Fires when a column is hidden or "unhidden".
61135              * @param {ColumnModel} this
61136              * @param {Number} columnIndex The column index
61137              * @param {Boolean} hidden true if hidden, false otherwise
61138              */
61139             "hiddenchange": true,
61140             /**
61141          * @event columnmoved
61142          * Fires when a column is moved.
61143          * @param {ColumnModel} this
61144          * @param {Number} oldIndex
61145          * @param {Number} newIndex
61146          */
61147         "columnmoved" : true,
61148         /**
61149          * @event columlockchange
61150          * Fires when a column's locked state is changed
61151          * @param {ColumnModel} this
61152          * @param {Number} colIndex
61153          * @param {Boolean} locked true if locked
61154          */
61155         "columnlockchange" : true
61156     });
61157     Roo.grid.ColumnModel.superclass.constructor.call(this);
61158 };
61159 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61160     /**
61161      * @cfg {String} header The header text to display in the Grid view.
61162      */
61163         /**
61164      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61165      */
61166         /**
61167      * @cfg {String} smHeader Header at Bootsrap Small width
61168      */
61169         /**
61170      * @cfg {String} mdHeader Header at Bootsrap Medium width
61171      */
61172         /**
61173      * @cfg {String} lgHeader Header at Bootsrap Large width
61174      */
61175         /**
61176      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61177      */
61178     /**
61179      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61180      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61181      * specified, the column's index is used as an index into the Record's data Array.
61182      */
61183     /**
61184      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61185      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61186      */
61187     /**
61188      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61189      * Defaults to the value of the {@link #defaultSortable} property.
61190      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61191      */
61192     /**
61193      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61194      */
61195     /**
61196      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61197      */
61198     /**
61199      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61200      */
61201     /**
61202      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61203      */
61204     /**
61205      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61206      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61207      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61208      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61209      */
61210        /**
61211      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61212      */
61213     /**
61214      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61215      */
61216     /**
61217      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61218      */
61219     /**
61220      * @cfg {String} cursor (Optional)
61221      */
61222     /**
61223      * @cfg {String} tooltip (Optional)
61224      */
61225     /**
61226      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61227      */
61228     /**
61229      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61230      */
61231     /**
61232      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61233      */
61234     /**
61235      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61236      */
61237         /**
61238      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61239      */
61240     /**
61241      * Returns the id of the column at the specified index.
61242      * @param {Number} index The column index
61243      * @return {String} the id
61244      */
61245     getColumnId : function(index){
61246         return this.config[index].id;
61247     },
61248
61249     /**
61250      * Returns the column for a specified id.
61251      * @param {String} id The column id
61252      * @return {Object} the column
61253      */
61254     getColumnById : function(id){
61255         return this.lookup[id];
61256     },
61257
61258     
61259     /**
61260      * Returns the column Object for a specified dataIndex.
61261      * @param {String} dataIndex The column dataIndex
61262      * @return {Object|Boolean} the column or false if not found
61263      */
61264     getColumnByDataIndex: function(dataIndex){
61265         var index = this.findColumnIndex(dataIndex);
61266         return index > -1 ? this.config[index] : false;
61267     },
61268     
61269     /**
61270      * Returns the index for a specified column id.
61271      * @param {String} id The column id
61272      * @return {Number} the index, or -1 if not found
61273      */
61274     getIndexById : function(id){
61275         for(var i = 0, len = this.config.length; i < len; i++){
61276             if(this.config[i].id == id){
61277                 return i;
61278             }
61279         }
61280         return -1;
61281     },
61282     
61283     /**
61284      * Returns the index for a specified column dataIndex.
61285      * @param {String} dataIndex The column dataIndex
61286      * @return {Number} the index, or -1 if not found
61287      */
61288     
61289     findColumnIndex : function(dataIndex){
61290         for(var i = 0, len = this.config.length; i < len; i++){
61291             if(this.config[i].dataIndex == dataIndex){
61292                 return i;
61293             }
61294         }
61295         return -1;
61296     },
61297     
61298     
61299     moveColumn : function(oldIndex, newIndex){
61300         var c = this.config[oldIndex];
61301         this.config.splice(oldIndex, 1);
61302         this.config.splice(newIndex, 0, c);
61303         this.dataMap = null;
61304         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61305     },
61306
61307     isLocked : function(colIndex){
61308         return this.config[colIndex].locked === true;
61309     },
61310
61311     setLocked : function(colIndex, value, suppressEvent){
61312         if(this.isLocked(colIndex) == value){
61313             return;
61314         }
61315         this.config[colIndex].locked = value;
61316         if(!suppressEvent){
61317             this.fireEvent("columnlockchange", this, colIndex, value);
61318         }
61319     },
61320
61321     getTotalLockedWidth : function(){
61322         var totalWidth = 0;
61323         for(var i = 0; i < this.config.length; i++){
61324             if(this.isLocked(i) && !this.isHidden(i)){
61325                 this.totalWidth += this.getColumnWidth(i);
61326             }
61327         }
61328         return totalWidth;
61329     },
61330
61331     getLockedCount : function(){
61332         for(var i = 0, len = this.config.length; i < len; i++){
61333             if(!this.isLocked(i)){
61334                 return i;
61335             }
61336         }
61337         
61338         return this.config.length;
61339     },
61340
61341     /**
61342      * Returns the number of columns.
61343      * @return {Number}
61344      */
61345     getColumnCount : function(visibleOnly){
61346         if(visibleOnly === true){
61347             var c = 0;
61348             for(var i = 0, len = this.config.length; i < len; i++){
61349                 if(!this.isHidden(i)){
61350                     c++;
61351                 }
61352             }
61353             return c;
61354         }
61355         return this.config.length;
61356     },
61357
61358     /**
61359      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61360      * @param {Function} fn
61361      * @param {Object} scope (optional)
61362      * @return {Array} result
61363      */
61364     getColumnsBy : function(fn, scope){
61365         var r = [];
61366         for(var i = 0, len = this.config.length; i < len; i++){
61367             var c = this.config[i];
61368             if(fn.call(scope||this, c, i) === true){
61369                 r[r.length] = c;
61370             }
61371         }
61372         return r;
61373     },
61374
61375     /**
61376      * Returns true if the specified column is sortable.
61377      * @param {Number} col The column index
61378      * @return {Boolean}
61379      */
61380     isSortable : function(col){
61381         if(typeof this.config[col].sortable == "undefined"){
61382             return this.defaultSortable;
61383         }
61384         return this.config[col].sortable;
61385     },
61386
61387     /**
61388      * Returns the rendering (formatting) function defined for the column.
61389      * @param {Number} col The column index.
61390      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61391      */
61392     getRenderer : function(col){
61393         if(!this.config[col].renderer){
61394             return Roo.grid.ColumnModel.defaultRenderer;
61395         }
61396         return this.config[col].renderer;
61397     },
61398
61399     /**
61400      * Sets the rendering (formatting) function for a column.
61401      * @param {Number} col The column index
61402      * @param {Function} fn The function to use to process the cell's raw data
61403      * to return HTML markup for the grid view. The render function is called with
61404      * the following parameters:<ul>
61405      * <li>Data value.</li>
61406      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61407      * <li>css A CSS style string to apply to the table cell.</li>
61408      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61409      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61410      * <li>Row index</li>
61411      * <li>Column index</li>
61412      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61413      */
61414     setRenderer : function(col, fn){
61415         this.config[col].renderer = fn;
61416     },
61417
61418     /**
61419      * Returns the width for the specified column.
61420      * @param {Number} col The column index
61421      * @param (optional) {String} gridSize bootstrap width size.
61422      * @return {Number}
61423      */
61424     getColumnWidth : function(col, gridSize)
61425         {
61426                 var cfg = this.config[col];
61427                 
61428                 if (typeof(gridSize) == 'undefined') {
61429                         return cfg.width * 1 || this.defaultWidth;
61430                 }
61431                 if (gridSize === false) { // if we set it..
61432                         return cfg.width || false;
61433                 }
61434                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61435                 
61436                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61437                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61438                                 continue;
61439                         }
61440                         return cfg[ sizes[i] ];
61441                 }
61442                 return 1;
61443                 
61444     },
61445
61446     /**
61447      * Sets the width for a column.
61448      * @param {Number} col The column index
61449      * @param {Number} width The new width
61450      */
61451     setColumnWidth : function(col, width, suppressEvent){
61452         this.config[col].width = width;
61453         this.totalWidth = null;
61454         if(!suppressEvent){
61455              this.fireEvent("widthchange", this, col, width);
61456         }
61457     },
61458
61459     /**
61460      * Returns the total width of all columns.
61461      * @param {Boolean} includeHidden True to include hidden column widths
61462      * @return {Number}
61463      */
61464     getTotalWidth : function(includeHidden){
61465         if(!this.totalWidth){
61466             this.totalWidth = 0;
61467             for(var i = 0, len = this.config.length; i < len; i++){
61468                 if(includeHidden || !this.isHidden(i)){
61469                     this.totalWidth += this.getColumnWidth(i);
61470                 }
61471             }
61472         }
61473         return this.totalWidth;
61474     },
61475
61476     /**
61477      * Returns the header for the specified column.
61478      * @param {Number} col The column index
61479      * @return {String}
61480      */
61481     getColumnHeader : function(col){
61482         return this.config[col].header;
61483     },
61484
61485     /**
61486      * Sets the header for a column.
61487      * @param {Number} col The column index
61488      * @param {String} header The new header
61489      */
61490     setColumnHeader : function(col, header){
61491         this.config[col].header = header;
61492         this.fireEvent("headerchange", this, col, header);
61493     },
61494
61495     /**
61496      * Returns the tooltip for the specified column.
61497      * @param {Number} col The column index
61498      * @return {String}
61499      */
61500     getColumnTooltip : function(col){
61501             return this.config[col].tooltip;
61502     },
61503     /**
61504      * Sets the tooltip for a column.
61505      * @param {Number} col The column index
61506      * @param {String} tooltip The new tooltip
61507      */
61508     setColumnTooltip : function(col, tooltip){
61509             this.config[col].tooltip = tooltip;
61510     },
61511
61512     /**
61513      * Returns the dataIndex for the specified column.
61514      * @param {Number} col The column index
61515      * @return {Number}
61516      */
61517     getDataIndex : function(col){
61518         return this.config[col].dataIndex;
61519     },
61520
61521     /**
61522      * Sets the dataIndex for a column.
61523      * @param {Number} col The column index
61524      * @param {Number} dataIndex The new dataIndex
61525      */
61526     setDataIndex : function(col, dataIndex){
61527         this.config[col].dataIndex = dataIndex;
61528     },
61529
61530     
61531     
61532     /**
61533      * Returns true if the cell is editable.
61534      * @param {Number} colIndex The column index
61535      * @param {Number} rowIndex The row index - this is nto actually used..?
61536      * @return {Boolean}
61537      */
61538     isCellEditable : function(colIndex, rowIndex){
61539         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61540     },
61541
61542     /**
61543      * Returns the editor defined for the cell/column.
61544      * return false or null to disable editing.
61545      * @param {Number} colIndex The column index
61546      * @param {Number} rowIndex The row index
61547      * @return {Object}
61548      */
61549     getCellEditor : function(colIndex, rowIndex){
61550         return this.config[colIndex].editor;
61551     },
61552
61553     /**
61554      * Sets if a column is editable.
61555      * @param {Number} col The column index
61556      * @param {Boolean} editable True if the column is editable
61557      */
61558     setEditable : function(col, editable){
61559         this.config[col].editable = editable;
61560     },
61561
61562
61563     /**
61564      * Returns true if the column is hidden.
61565      * @param {Number} colIndex The column index
61566      * @return {Boolean}
61567      */
61568     isHidden : function(colIndex){
61569         return this.config[colIndex].hidden;
61570     },
61571
61572
61573     /**
61574      * Returns true if the column width cannot be changed
61575      */
61576     isFixed : function(colIndex){
61577         return this.config[colIndex].fixed;
61578     },
61579
61580     /**
61581      * Returns true if the column can be resized
61582      * @return {Boolean}
61583      */
61584     isResizable : function(colIndex){
61585         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61586     },
61587     /**
61588      * Sets if a column is hidden.
61589      * @param {Number} colIndex The column index
61590      * @param {Boolean} hidden True if the column is hidden
61591      */
61592     setHidden : function(colIndex, hidden){
61593         this.config[colIndex].hidden = hidden;
61594         this.totalWidth = null;
61595         this.fireEvent("hiddenchange", this, colIndex, hidden);
61596     },
61597
61598     /**
61599      * Sets the editor for a column.
61600      * @param {Number} col The column index
61601      * @param {Object} editor The editor object
61602      */
61603     setEditor : function(col, editor){
61604         this.config[col].editor = editor;
61605     },
61606     /**
61607      * Add a column (experimental...) - defaults to adding to the end..
61608      * @param {Object} config 
61609     */
61610     addColumn : function(c)
61611     {
61612     
61613         var i = this.config.length;
61614         this.config[i] = c;
61615         
61616         if(typeof c.dataIndex == "undefined"){
61617             c.dataIndex = i;
61618         }
61619         if(typeof c.renderer == "string"){
61620             c.renderer = Roo.util.Format[c.renderer];
61621         }
61622         if(typeof c.id == "undefined"){
61623             c.id = Roo.id();
61624         }
61625         if(c.editor && c.editor.xtype){
61626             c.editor  = Roo.factory(c.editor, Roo.grid);
61627         }
61628         if(c.editor && c.editor.isFormField){
61629             c.editor = new Roo.grid.GridEditor(c.editor);
61630         }
61631         this.lookup[c.id] = c;
61632     }
61633     
61634 });
61635
61636 Roo.grid.ColumnModel.defaultRenderer = function(value)
61637 {
61638     if(typeof value == "object") {
61639         return value;
61640     }
61641         if(typeof value == "string" && value.length < 1){
61642             return "&#160;";
61643         }
61644     
61645         return String.format("{0}", value);
61646 };
61647
61648 // Alias for backwards compatibility
61649 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61650 /*
61651  * Based on:
61652  * Ext JS Library 1.1.1
61653  * Copyright(c) 2006-2007, Ext JS, LLC.
61654  *
61655  * Originally Released Under LGPL - original licence link has changed is not relivant.
61656  *
61657  * Fork - LGPL
61658  * <script type="text/javascript">
61659  */
61660
61661 /**
61662  * @class Roo.grid.AbstractSelectionModel
61663  * @extends Roo.util.Observable
61664  * @abstract
61665  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61666  * implemented by descendant classes.  This class should not be directly instantiated.
61667  * @constructor
61668  */
61669 Roo.grid.AbstractSelectionModel = function(){
61670     this.locked = false;
61671     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61672 };
61673
61674 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61675     /** @ignore Called by the grid automatically. Do not call directly. */
61676     init : function(grid){
61677         this.grid = grid;
61678         this.initEvents();
61679     },
61680
61681     /**
61682      * Locks the selections.
61683      */
61684     lock : function(){
61685         this.locked = true;
61686     },
61687
61688     /**
61689      * Unlocks the selections.
61690      */
61691     unlock : function(){
61692         this.locked = false;
61693     },
61694
61695     /**
61696      * Returns true if the selections are locked.
61697      * @return {Boolean}
61698      */
61699     isLocked : function(){
61700         return this.locked;
61701     }
61702 });/*
61703  * Based on:
61704  * Ext JS Library 1.1.1
61705  * Copyright(c) 2006-2007, Ext JS, LLC.
61706  *
61707  * Originally Released Under LGPL - original licence link has changed is not relivant.
61708  *
61709  * Fork - LGPL
61710  * <script type="text/javascript">
61711  */
61712 /**
61713  * @extends Roo.grid.AbstractSelectionModel
61714  * @class Roo.grid.RowSelectionModel
61715  * The default SelectionModel used by {@link Roo.grid.Grid}.
61716  * It supports multiple selections and keyboard selection/navigation. 
61717  * @constructor
61718  * @param {Object} config
61719  */
61720 Roo.grid.RowSelectionModel = function(config){
61721     Roo.apply(this, config);
61722     this.selections = new Roo.util.MixedCollection(false, function(o){
61723         return o.id;
61724     });
61725
61726     this.last = false;
61727     this.lastActive = false;
61728
61729     this.addEvents({
61730         /**
61731         * @event selectionchange
61732         * Fires when the selection changes
61733         * @param {SelectionModel} this
61734         */
61735        "selectionchange" : true,
61736        /**
61737         * @event afterselectionchange
61738         * Fires after the selection changes (eg. by key press or clicking)
61739         * @param {SelectionModel} this
61740         */
61741        "afterselectionchange" : true,
61742        /**
61743         * @event beforerowselect
61744         * Fires when a row is selected being selected, return false to cancel.
61745         * @param {SelectionModel} this
61746         * @param {Number} rowIndex The selected index
61747         * @param {Boolean} keepExisting False if other selections will be cleared
61748         */
61749        "beforerowselect" : true,
61750        /**
61751         * @event rowselect
61752         * Fires when a row is selected.
61753         * @param {SelectionModel} this
61754         * @param {Number} rowIndex The selected index
61755         * @param {Roo.data.Record} r The record
61756         */
61757        "rowselect" : true,
61758        /**
61759         * @event rowdeselect
61760         * Fires when a row is deselected.
61761         * @param {SelectionModel} this
61762         * @param {Number} rowIndex The selected index
61763         */
61764         "rowdeselect" : true
61765     });
61766     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61767     this.locked = false;
61768 };
61769
61770 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61771     /**
61772      * @cfg {Boolean} singleSelect
61773      * True to allow selection of only one row at a time (defaults to false)
61774      */
61775     singleSelect : false,
61776
61777     // private
61778     initEvents : function(){
61779
61780         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61781             this.grid.on("mousedown", this.handleMouseDown, this);
61782         }else{ // allow click to work like normal
61783             this.grid.on("rowclick", this.handleDragableRowClick, this);
61784         }
61785         // bootstrap does not have a view..
61786         var view = this.grid.view ? this.grid.view : this.grid;
61787         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61788             "up" : function(e){
61789                 if(!e.shiftKey){
61790                     this.selectPrevious(e.shiftKey);
61791                 }else if(this.last !== false && this.lastActive !== false){
61792                     var last = this.last;
61793                     this.selectRange(this.last,  this.lastActive-1);
61794                     view.focusRow(this.lastActive);
61795                     if(last !== false){
61796                         this.last = last;
61797                     }
61798                 }else{
61799                     this.selectFirstRow();
61800                 }
61801                 this.fireEvent("afterselectionchange", this);
61802             },
61803             "down" : function(e){
61804                 if(!e.shiftKey){
61805                     this.selectNext(e.shiftKey);
61806                 }else if(this.last !== false && this.lastActive !== false){
61807                     var last = this.last;
61808                     this.selectRange(this.last,  this.lastActive+1);
61809                     view.focusRow(this.lastActive);
61810                     if(last !== false){
61811                         this.last = last;
61812                     }
61813                 }else{
61814                     this.selectFirstRow();
61815                 }
61816                 this.fireEvent("afterselectionchange", this);
61817             },
61818             scope: this
61819         });
61820
61821          
61822         view.on("refresh", this.onRefresh, this);
61823         view.on("rowupdated", this.onRowUpdated, this);
61824         view.on("rowremoved", this.onRemove, this);
61825     },
61826
61827     // private
61828     onRefresh : function(){
61829         var ds = this.grid.ds, i, v = this.grid.view;
61830         var s = this.selections;
61831         s.each(function(r){
61832             if((i = ds.indexOfId(r.id)) != -1){
61833                 v.onRowSelect(i);
61834                 s.add(ds.getAt(i)); // updating the selection relate data
61835             }else{
61836                 s.remove(r);
61837             }
61838         });
61839     },
61840
61841     // private
61842     onRemove : function(v, index, r){
61843         this.selections.remove(r);
61844     },
61845
61846     // private
61847     onRowUpdated : function(v, index, r){
61848         if(this.isSelected(r)){
61849             v.onRowSelect(index);
61850         }
61851     },
61852
61853     /**
61854      * Select records.
61855      * @param {Array} records The records to select
61856      * @param {Boolean} keepExisting (optional) True to keep existing selections
61857      */
61858     selectRecords : function(records, keepExisting){
61859         if(!keepExisting){
61860             this.clearSelections();
61861         }
61862         var ds = this.grid.ds;
61863         for(var i = 0, len = records.length; i < len; i++){
61864             this.selectRow(ds.indexOf(records[i]), true);
61865         }
61866     },
61867
61868     /**
61869      * Gets the number of selected rows.
61870      * @return {Number}
61871      */
61872     getCount : function(){
61873         return this.selections.length;
61874     },
61875
61876     /**
61877      * Selects the first row in the grid.
61878      */
61879     selectFirstRow : function(){
61880         this.selectRow(0);
61881     },
61882
61883     /**
61884      * Select the last row.
61885      * @param {Boolean} keepExisting (optional) True to keep existing selections
61886      */
61887     selectLastRow : function(keepExisting){
61888         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61889     },
61890
61891     /**
61892      * Selects the row immediately following the last selected row.
61893      * @param {Boolean} keepExisting (optional) True to keep existing selections
61894      */
61895     selectNext : function(keepExisting){
61896         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61897             this.selectRow(this.last+1, keepExisting);
61898             var view = this.grid.view ? this.grid.view : this.grid;
61899             view.focusRow(this.last);
61900         }
61901     },
61902
61903     /**
61904      * Selects the row that precedes the last selected row.
61905      * @param {Boolean} keepExisting (optional) True to keep existing selections
61906      */
61907     selectPrevious : function(keepExisting){
61908         if(this.last){
61909             this.selectRow(this.last-1, keepExisting);
61910             var view = this.grid.view ? this.grid.view : this.grid;
61911             view.focusRow(this.last);
61912         }
61913     },
61914
61915     /**
61916      * Returns the selected records
61917      * @return {Array} Array of selected records
61918      */
61919     getSelections : function(){
61920         return [].concat(this.selections.items);
61921     },
61922
61923     /**
61924      * Returns the first selected record.
61925      * @return {Record}
61926      */
61927     getSelected : function(){
61928         return this.selections.itemAt(0);
61929     },
61930
61931
61932     /**
61933      * Clears all selections.
61934      */
61935     clearSelections : function(fast){
61936         if(this.locked) {
61937             return;
61938         }
61939         if(fast !== true){
61940             var ds = this.grid.ds;
61941             var s = this.selections;
61942             s.each(function(r){
61943                 this.deselectRow(ds.indexOfId(r.id));
61944             }, this);
61945             s.clear();
61946         }else{
61947             this.selections.clear();
61948         }
61949         this.last = false;
61950     },
61951
61952
61953     /**
61954      * Selects all rows.
61955      */
61956     selectAll : function(){
61957         if(this.locked) {
61958             return;
61959         }
61960         this.selections.clear();
61961         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61962             this.selectRow(i, true);
61963         }
61964     },
61965
61966     /**
61967      * Returns True if there is a selection.
61968      * @return {Boolean}
61969      */
61970     hasSelection : function(){
61971         return this.selections.length > 0;
61972     },
61973
61974     /**
61975      * Returns True if the specified row is selected.
61976      * @param {Number/Record} record The record or index of the record to check
61977      * @return {Boolean}
61978      */
61979     isSelected : function(index){
61980         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61981         return (r && this.selections.key(r.id) ? true : false);
61982     },
61983
61984     /**
61985      * Returns True if the specified record id is selected.
61986      * @param {String} id The id of record to check
61987      * @return {Boolean}
61988      */
61989     isIdSelected : function(id){
61990         return (this.selections.key(id) ? true : false);
61991     },
61992
61993     // private
61994     handleMouseDown : function(e, t)
61995     {
61996         var view = this.grid.view ? this.grid.view : this.grid;
61997         var rowIndex;
61998         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61999             return;
62000         };
62001         if(e.shiftKey && this.last !== false){
62002             var last = this.last;
62003             this.selectRange(last, rowIndex, e.ctrlKey);
62004             this.last = last; // reset the last
62005             view.focusRow(rowIndex);
62006         }else{
62007             var isSelected = this.isSelected(rowIndex);
62008             if(e.button !== 0 && isSelected){
62009                 view.focusRow(rowIndex);
62010             }else if(e.ctrlKey && isSelected){
62011                 this.deselectRow(rowIndex);
62012             }else if(!isSelected){
62013                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62014                 view.focusRow(rowIndex);
62015             }
62016         }
62017         this.fireEvent("afterselectionchange", this);
62018     },
62019     // private
62020     handleDragableRowClick :  function(grid, rowIndex, e) 
62021     {
62022         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62023             this.selectRow(rowIndex, false);
62024             var view = this.grid.view ? this.grid.view : this.grid;
62025             view.focusRow(rowIndex);
62026              this.fireEvent("afterselectionchange", this);
62027         }
62028     },
62029     
62030     /**
62031      * Selects multiple rows.
62032      * @param {Array} rows Array of the indexes of the row to select
62033      * @param {Boolean} keepExisting (optional) True to keep existing selections
62034      */
62035     selectRows : function(rows, keepExisting){
62036         if(!keepExisting){
62037             this.clearSelections();
62038         }
62039         for(var i = 0, len = rows.length; i < len; i++){
62040             this.selectRow(rows[i], true);
62041         }
62042     },
62043
62044     /**
62045      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62046      * @param {Number} startRow The index of the first row in the range
62047      * @param {Number} endRow The index of the last row in the range
62048      * @param {Boolean} keepExisting (optional) True to retain existing selections
62049      */
62050     selectRange : function(startRow, endRow, keepExisting){
62051         if(this.locked) {
62052             return;
62053         }
62054         if(!keepExisting){
62055             this.clearSelections();
62056         }
62057         if(startRow <= endRow){
62058             for(var i = startRow; i <= endRow; i++){
62059                 this.selectRow(i, true);
62060             }
62061         }else{
62062             for(var i = startRow; i >= endRow; i--){
62063                 this.selectRow(i, true);
62064             }
62065         }
62066     },
62067
62068     /**
62069      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62070      * @param {Number} startRow The index of the first row in the range
62071      * @param {Number} endRow The index of the last row in the range
62072      */
62073     deselectRange : function(startRow, endRow, preventViewNotify){
62074         if(this.locked) {
62075             return;
62076         }
62077         for(var i = startRow; i <= endRow; i++){
62078             this.deselectRow(i, preventViewNotify);
62079         }
62080     },
62081
62082     /**
62083      * Selects a row.
62084      * @param {Number} row The index of the row to select
62085      * @param {Boolean} keepExisting (optional) True to keep existing selections
62086      */
62087     selectRow : function(index, keepExisting, preventViewNotify){
62088         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62089             return;
62090         }
62091         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62092             if(!keepExisting || this.singleSelect){
62093                 this.clearSelections();
62094             }
62095             var r = this.grid.ds.getAt(index);
62096             this.selections.add(r);
62097             this.last = this.lastActive = index;
62098             if(!preventViewNotify){
62099                 var view = this.grid.view ? this.grid.view : this.grid;
62100                 view.onRowSelect(index);
62101             }
62102             this.fireEvent("rowselect", this, index, r);
62103             this.fireEvent("selectionchange", this);
62104         }
62105     },
62106
62107     /**
62108      * Deselects a row.
62109      * @param {Number} row The index of the row to deselect
62110      */
62111     deselectRow : function(index, preventViewNotify){
62112         if(this.locked) {
62113             return;
62114         }
62115         if(this.last == index){
62116             this.last = false;
62117         }
62118         if(this.lastActive == index){
62119             this.lastActive = false;
62120         }
62121         var r = this.grid.ds.getAt(index);
62122         this.selections.remove(r);
62123         if(!preventViewNotify){
62124             var view = this.grid.view ? this.grid.view : this.grid;
62125             view.onRowDeselect(index);
62126         }
62127         this.fireEvent("rowdeselect", this, index);
62128         this.fireEvent("selectionchange", this);
62129     },
62130
62131     // private
62132     restoreLast : function(){
62133         if(this._last){
62134             this.last = this._last;
62135         }
62136     },
62137
62138     // private
62139     acceptsNav : function(row, col, cm){
62140         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62141     },
62142
62143     // private
62144     onEditorKey : function(field, e){
62145         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62146         if(k == e.TAB){
62147             e.stopEvent();
62148             ed.completeEdit();
62149             if(e.shiftKey){
62150                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62151             }else{
62152                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62153             }
62154         }else if(k == e.ENTER && !e.ctrlKey){
62155             e.stopEvent();
62156             ed.completeEdit();
62157             if(e.shiftKey){
62158                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62159             }else{
62160                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62161             }
62162         }else if(k == e.ESC){
62163             ed.cancelEdit();
62164         }
62165         if(newCell){
62166             g.startEditing(newCell[0], newCell[1]);
62167         }
62168     }
62169 });/*
62170  * Based on:
62171  * Ext JS Library 1.1.1
62172  * Copyright(c) 2006-2007, Ext JS, LLC.
62173  *
62174  * Originally Released Under LGPL - original licence link has changed is not relivant.
62175  *
62176  * Fork - LGPL
62177  * <script type="text/javascript">
62178  */
62179 /**
62180  * @class Roo.grid.CellSelectionModel
62181  * @extends Roo.grid.AbstractSelectionModel
62182  * This class provides the basic implementation for cell selection in a grid.
62183  * @constructor
62184  * @param {Object} config The object containing the configuration of this model.
62185  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62186  */
62187 Roo.grid.CellSelectionModel = function(config){
62188     Roo.apply(this, config);
62189
62190     this.selection = null;
62191
62192     this.addEvents({
62193         /**
62194              * @event beforerowselect
62195              * Fires before a cell is selected.
62196              * @param {SelectionModel} this
62197              * @param {Number} rowIndex The selected row index
62198              * @param {Number} colIndex The selected cell index
62199              */
62200             "beforecellselect" : true,
62201         /**
62202              * @event cellselect
62203              * Fires when a cell is selected.
62204              * @param {SelectionModel} this
62205              * @param {Number} rowIndex The selected row index
62206              * @param {Number} colIndex The selected cell index
62207              */
62208             "cellselect" : true,
62209         /**
62210              * @event selectionchange
62211              * Fires when the active selection changes.
62212              * @param {SelectionModel} this
62213              * @param {Object} selection null for no selection or an object (o) with two properties
62214                 <ul>
62215                 <li>o.record: the record object for the row the selection is in</li>
62216                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62217                 </ul>
62218              */
62219             "selectionchange" : true,
62220         /**
62221              * @event tabend
62222              * Fires when the tab (or enter) was pressed on the last editable cell
62223              * You can use this to trigger add new row.
62224              * @param {SelectionModel} this
62225              */
62226             "tabend" : true,
62227          /**
62228              * @event beforeeditnext
62229              * Fires before the next editable sell is made active
62230              * You can use this to skip to another cell or fire the tabend
62231              *    if you set cell to false
62232              * @param {Object} eventdata object : { cell : [ row, col ] } 
62233              */
62234             "beforeeditnext" : true
62235     });
62236     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62237 };
62238
62239 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62240     
62241     enter_is_tab: false,
62242
62243     /** @ignore */
62244     initEvents : function(){
62245         this.grid.on("mousedown", this.handleMouseDown, this);
62246         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62247         var view = this.grid.view;
62248         view.on("refresh", this.onViewChange, this);
62249         view.on("rowupdated", this.onRowUpdated, this);
62250         view.on("beforerowremoved", this.clearSelections, this);
62251         view.on("beforerowsinserted", this.clearSelections, this);
62252         if(this.grid.isEditor){
62253             this.grid.on("beforeedit", this.beforeEdit,  this);
62254         }
62255     },
62256
62257         //private
62258     beforeEdit : function(e){
62259         this.select(e.row, e.column, false, true, e.record);
62260     },
62261
62262         //private
62263     onRowUpdated : function(v, index, r){
62264         if(this.selection && this.selection.record == r){
62265             v.onCellSelect(index, this.selection.cell[1]);
62266         }
62267     },
62268
62269         //private
62270     onViewChange : function(){
62271         this.clearSelections(true);
62272     },
62273
62274         /**
62275          * Returns the currently selected cell,.
62276          * @return {Array} The selected cell (row, column) or null if none selected.
62277          */
62278     getSelectedCell : function(){
62279         return this.selection ? this.selection.cell : null;
62280     },
62281
62282     /**
62283      * Clears all selections.
62284      * @param {Boolean} true to prevent the gridview from being notified about the change.
62285      */
62286     clearSelections : function(preventNotify){
62287         var s = this.selection;
62288         if(s){
62289             if(preventNotify !== true){
62290                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62291             }
62292             this.selection = null;
62293             this.fireEvent("selectionchange", this, null);
62294         }
62295     },
62296
62297     /**
62298      * Returns true if there is a selection.
62299      * @return {Boolean}
62300      */
62301     hasSelection : function(){
62302         return this.selection ? true : false;
62303     },
62304
62305     /** @ignore */
62306     handleMouseDown : function(e, t){
62307         var v = this.grid.getView();
62308         if(this.isLocked()){
62309             return;
62310         };
62311         var row = v.findRowIndex(t);
62312         var cell = v.findCellIndex(t);
62313         if(row !== false && cell !== false){
62314             this.select(row, cell);
62315         }
62316     },
62317
62318     /**
62319      * Selects a cell.
62320      * @param {Number} rowIndex
62321      * @param {Number} collIndex
62322      */
62323     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62324         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62325             this.clearSelections();
62326             r = r || this.grid.dataSource.getAt(rowIndex);
62327             this.selection = {
62328                 record : r,
62329                 cell : [rowIndex, colIndex]
62330             };
62331             if(!preventViewNotify){
62332                 var v = this.grid.getView();
62333                 v.onCellSelect(rowIndex, colIndex);
62334                 if(preventFocus !== true){
62335                     v.focusCell(rowIndex, colIndex);
62336                 }
62337             }
62338             this.fireEvent("cellselect", this, rowIndex, colIndex);
62339             this.fireEvent("selectionchange", this, this.selection);
62340         }
62341     },
62342
62343         //private
62344     isSelectable : function(rowIndex, colIndex, cm){
62345         return !cm.isHidden(colIndex);
62346     },
62347
62348     /** @ignore */
62349     handleKeyDown : function(e){
62350         //Roo.log('Cell Sel Model handleKeyDown');
62351         if(!e.isNavKeyPress()){
62352             return;
62353         }
62354         var g = this.grid, s = this.selection;
62355         if(!s){
62356             e.stopEvent();
62357             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62358             if(cell){
62359                 this.select(cell[0], cell[1]);
62360             }
62361             return;
62362         }
62363         var sm = this;
62364         var walk = function(row, col, step){
62365             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62366         };
62367         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62368         var newCell;
62369
62370       
62371
62372         switch(k){
62373             case e.TAB:
62374                 // handled by onEditorKey
62375                 if (g.isEditor && g.editing) {
62376                     return;
62377                 }
62378                 if(e.shiftKey) {
62379                     newCell = walk(r, c-1, -1);
62380                 } else {
62381                     newCell = walk(r, c+1, 1);
62382                 }
62383                 break;
62384             
62385             case e.DOWN:
62386                newCell = walk(r+1, c, 1);
62387                 break;
62388             
62389             case e.UP:
62390                 newCell = walk(r-1, c, -1);
62391                 break;
62392             
62393             case e.RIGHT:
62394                 newCell = walk(r, c+1, 1);
62395                 break;
62396             
62397             case e.LEFT:
62398                 newCell = walk(r, c-1, -1);
62399                 break;
62400             
62401             case e.ENTER:
62402                 
62403                 if(g.isEditor && !g.editing){
62404                    g.startEditing(r, c);
62405                    e.stopEvent();
62406                    return;
62407                 }
62408                 
62409                 
62410              break;
62411         };
62412         if(newCell){
62413             this.select(newCell[0], newCell[1]);
62414             e.stopEvent();
62415             
62416         }
62417     },
62418
62419     acceptsNav : function(row, col, cm){
62420         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62421     },
62422     /**
62423      * Selects a cell.
62424      * @param {Number} field (not used) - as it's normally used as a listener
62425      * @param {Number} e - event - fake it by using
62426      *
62427      * var e = Roo.EventObjectImpl.prototype;
62428      * e.keyCode = e.TAB
62429      *
62430      * 
62431      */
62432     onEditorKey : function(field, e){
62433         
62434         var k = e.getKey(),
62435             newCell,
62436             g = this.grid,
62437             ed = g.activeEditor,
62438             forward = false;
62439         ///Roo.log('onEditorKey' + k);
62440         
62441         
62442         if (this.enter_is_tab && k == e.ENTER) {
62443             k = e.TAB;
62444         }
62445         
62446         if(k == e.TAB){
62447             if(e.shiftKey){
62448                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62449             }else{
62450                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62451                 forward = true;
62452             }
62453             
62454             e.stopEvent();
62455             
62456         } else if(k == e.ENTER &&  !e.ctrlKey){
62457             ed.completeEdit();
62458             e.stopEvent();
62459             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62460         
62461                 } else if(k == e.ESC){
62462             ed.cancelEdit();
62463         }
62464                 
62465         if (newCell) {
62466             var ecall = { cell : newCell, forward : forward };
62467             this.fireEvent('beforeeditnext', ecall );
62468             newCell = ecall.cell;
62469                         forward = ecall.forward;
62470         }
62471                 
62472         if(newCell){
62473             //Roo.log('next cell after edit');
62474             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62475         } else if (forward) {
62476             // tabbed past last
62477             this.fireEvent.defer(100, this, ['tabend',this]);
62478         }
62479     }
62480 });/*
62481  * Based on:
62482  * Ext JS Library 1.1.1
62483  * Copyright(c) 2006-2007, Ext JS, LLC.
62484  *
62485  * Originally Released Under LGPL - original licence link has changed is not relivant.
62486  *
62487  * Fork - LGPL
62488  * <script type="text/javascript">
62489  */
62490  
62491 /**
62492  * @class Roo.grid.EditorGrid
62493  * @extends Roo.grid.Grid
62494  * Class for creating and editable grid.
62495  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62496  * The container MUST have some type of size defined for the grid to fill. The container will be 
62497  * automatically set to position relative if it isn't already.
62498  * @param {Object} dataSource The data model to bind to
62499  * @param {Object} colModel The column model with info about this grid's columns
62500  */
62501 Roo.grid.EditorGrid = function(container, config){
62502     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62503     this.getGridEl().addClass("xedit-grid");
62504
62505     if(!this.selModel){
62506         this.selModel = new Roo.grid.CellSelectionModel();
62507     }
62508
62509     this.activeEditor = null;
62510
62511         this.addEvents({
62512             /**
62513              * @event beforeedit
62514              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62515              * <ul style="padding:5px;padding-left:16px;">
62516              * <li>grid - This grid</li>
62517              * <li>record - The record being edited</li>
62518              * <li>field - The field name being edited</li>
62519              * <li>value - The value for the field being edited.</li>
62520              * <li>row - The grid row index</li>
62521              * <li>column - The grid column index</li>
62522              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62523              * </ul>
62524              * @param {Object} e An edit event (see above for description)
62525              */
62526             "beforeedit" : true,
62527             /**
62528              * @event afteredit
62529              * Fires after a cell is edited. <br />
62530              * <ul style="padding:5px;padding-left:16px;">
62531              * <li>grid - This grid</li>
62532              * <li>record - The record being edited</li>
62533              * <li>field - The field name being edited</li>
62534              * <li>value - The value being set</li>
62535              * <li>originalValue - The original value for the field, before the edit.</li>
62536              * <li>row - The grid row index</li>
62537              * <li>column - The grid column index</li>
62538              * </ul>
62539              * @param {Object} e An edit event (see above for description)
62540              */
62541             "afteredit" : true,
62542             /**
62543              * @event validateedit
62544              * Fires after a cell is edited, but before the value is set in the record. 
62545          * You can use this to modify the value being set in the field, Return false
62546              * to cancel the change. The edit event object has the following properties <br />
62547              * <ul style="padding:5px;padding-left:16px;">
62548          * <li>editor - This editor</li>
62549              * <li>grid - This grid</li>
62550              * <li>record - The record being edited</li>
62551              * <li>field - The field name being edited</li>
62552              * <li>value - The value being set</li>
62553              * <li>originalValue - The original value for the field, before the edit.</li>
62554              * <li>row - The grid row index</li>
62555              * <li>column - The grid column index</li>
62556              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62557              * </ul>
62558              * @param {Object} e An edit event (see above for description)
62559              */
62560             "validateedit" : true
62561         });
62562     this.on("bodyscroll", this.stopEditing,  this);
62563     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62564 };
62565
62566 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62567     /**
62568      * @cfg {Number} clicksToEdit
62569      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62570      */
62571     clicksToEdit: 2,
62572
62573     // private
62574     isEditor : true,
62575     // private
62576     trackMouseOver: false, // causes very odd FF errors
62577
62578     onCellDblClick : function(g, row, col){
62579         this.startEditing(row, col);
62580     },
62581
62582     onEditComplete : function(ed, value, startValue){
62583         this.editing = false;
62584         this.activeEditor = null;
62585         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62586         var r = ed.record;
62587         var field = this.colModel.getDataIndex(ed.col);
62588         var e = {
62589             grid: this,
62590             record: r,
62591             field: field,
62592             originalValue: startValue,
62593             value: value,
62594             row: ed.row,
62595             column: ed.col,
62596             cancel:false,
62597             editor: ed
62598         };
62599         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62600         cell.show();
62601           
62602         if(String(value) !== String(startValue)){
62603             
62604             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62605                 r.set(field, e.value);
62606                 // if we are dealing with a combo box..
62607                 // then we also set the 'name' colum to be the displayField
62608                 if (ed.field.displayField && ed.field.name) {
62609                     r.set(ed.field.name, ed.field.el.dom.value);
62610                 }
62611                 
62612                 delete e.cancel; //?? why!!!
62613                 this.fireEvent("afteredit", e);
62614             }
62615         } else {
62616             this.fireEvent("afteredit", e); // always fire it!
62617         }
62618         this.view.focusCell(ed.row, ed.col);
62619     },
62620
62621     /**
62622      * Starts editing the specified for the specified row/column
62623      * @param {Number} rowIndex
62624      * @param {Number} colIndex
62625      */
62626     startEditing : function(row, col){
62627         this.stopEditing();
62628         if(this.colModel.isCellEditable(col, row)){
62629             this.view.ensureVisible(row, col, true);
62630           
62631             var r = this.dataSource.getAt(row);
62632             var field = this.colModel.getDataIndex(col);
62633             var cell = Roo.get(this.view.getCell(row,col));
62634             var e = {
62635                 grid: this,
62636                 record: r,
62637                 field: field,
62638                 value: r.data[field],
62639                 row: row,
62640                 column: col,
62641                 cancel:false 
62642             };
62643             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62644                 this.editing = true;
62645                 var ed = this.colModel.getCellEditor(col, row);
62646                 
62647                 if (!ed) {
62648                     return;
62649                 }
62650                 if(!ed.rendered){
62651                     ed.render(ed.parentEl || document.body);
62652                 }
62653                 ed.field.reset();
62654                
62655                 cell.hide();
62656                 
62657                 (function(){ // complex but required for focus issues in safari, ie and opera
62658                     ed.row = row;
62659                     ed.col = col;
62660                     ed.record = r;
62661                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62662                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62663                     this.activeEditor = ed;
62664                     var v = r.data[field];
62665                     ed.startEdit(this.view.getCell(row, col), v);
62666                     // combo's with 'displayField and name set
62667                     if (ed.field.displayField && ed.field.name) {
62668                         ed.field.el.dom.value = r.data[ed.field.name];
62669                     }
62670                     
62671                     
62672                 }).defer(50, this);
62673             }
62674         }
62675     },
62676         
62677     /**
62678      * Stops any active editing
62679      */
62680     stopEditing : function(){
62681         if(this.activeEditor){
62682             this.activeEditor.completeEdit();
62683         }
62684         this.activeEditor = null;
62685     },
62686         
62687          /**
62688      * Called to get grid's drag proxy text, by default returns this.ddText.
62689      * @return {String}
62690      */
62691     getDragDropText : function(){
62692         var count = this.selModel.getSelectedCell() ? 1 : 0;
62693         return String.format(this.ddText, count, count == 1 ? '' : 's');
62694     }
62695         
62696 });/*
62697  * Based on:
62698  * Ext JS Library 1.1.1
62699  * Copyright(c) 2006-2007, Ext JS, LLC.
62700  *
62701  * Originally Released Under LGPL - original licence link has changed is not relivant.
62702  *
62703  * Fork - LGPL
62704  * <script type="text/javascript">
62705  */
62706
62707 // private - not really -- you end up using it !
62708 // This is a support class used internally by the Grid components
62709
62710 /**
62711  * @class Roo.grid.GridEditor
62712  * @extends Roo.Editor
62713  * Class for creating and editable grid elements.
62714  * @param {Object} config any settings (must include field)
62715  */
62716 Roo.grid.GridEditor = function(field, config){
62717     if (!config && field.field) {
62718         config = field;
62719         field = Roo.factory(config.field, Roo.form);
62720     }
62721     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62722     field.monitorTab = false;
62723 };
62724
62725 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62726     
62727     /**
62728      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62729      */
62730     
62731     alignment: "tl-tl",
62732     autoSize: "width",
62733     hideEl : false,
62734     cls: "x-small-editor x-grid-editor",
62735     shim:false,
62736     shadow:"frame"
62737 });/*
62738  * Based on:
62739  * Ext JS Library 1.1.1
62740  * Copyright(c) 2006-2007, Ext JS, LLC.
62741  *
62742  * Originally Released Under LGPL - original licence link has changed is not relivant.
62743  *
62744  * Fork - LGPL
62745  * <script type="text/javascript">
62746  */
62747   
62748
62749   
62750 Roo.grid.PropertyRecord = Roo.data.Record.create([
62751     {name:'name',type:'string'},  'value'
62752 ]);
62753
62754
62755 Roo.grid.PropertyStore = function(grid, source){
62756     this.grid = grid;
62757     this.store = new Roo.data.Store({
62758         recordType : Roo.grid.PropertyRecord
62759     });
62760     this.store.on('update', this.onUpdate,  this);
62761     if(source){
62762         this.setSource(source);
62763     }
62764     Roo.grid.PropertyStore.superclass.constructor.call(this);
62765 };
62766
62767
62768
62769 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62770     setSource : function(o){
62771         this.source = o;
62772         this.store.removeAll();
62773         var data = [];
62774         for(var k in o){
62775             if(this.isEditableValue(o[k])){
62776                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62777             }
62778         }
62779         this.store.loadRecords({records: data}, {}, true);
62780     },
62781
62782     onUpdate : function(ds, record, type){
62783         if(type == Roo.data.Record.EDIT){
62784             var v = record.data['value'];
62785             var oldValue = record.modified['value'];
62786             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62787                 this.source[record.id] = v;
62788                 record.commit();
62789                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62790             }else{
62791                 record.reject();
62792             }
62793         }
62794     },
62795
62796     getProperty : function(row){
62797        return this.store.getAt(row);
62798     },
62799
62800     isEditableValue: function(val){
62801         if(val && val instanceof Date){
62802             return true;
62803         }else if(typeof val == 'object' || typeof val == 'function'){
62804             return false;
62805         }
62806         return true;
62807     },
62808
62809     setValue : function(prop, value){
62810         this.source[prop] = value;
62811         this.store.getById(prop).set('value', value);
62812     },
62813
62814     getSource : function(){
62815         return this.source;
62816     }
62817 });
62818
62819 Roo.grid.PropertyColumnModel = function(grid, store){
62820     this.grid = grid;
62821     var g = Roo.grid;
62822     g.PropertyColumnModel.superclass.constructor.call(this, [
62823         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62824         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62825     ]);
62826     this.store = store;
62827     this.bselect = Roo.DomHelper.append(document.body, {
62828         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62829             {tag: 'option', value: 'true', html: 'true'},
62830             {tag: 'option', value: 'false', html: 'false'}
62831         ]
62832     });
62833     Roo.id(this.bselect);
62834     var f = Roo.form;
62835     this.editors = {
62836         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62837         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62838         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62839         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62840         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62841     };
62842     this.renderCellDelegate = this.renderCell.createDelegate(this);
62843     this.renderPropDelegate = this.renderProp.createDelegate(this);
62844 };
62845
62846 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62847     
62848     
62849     nameText : 'Name',
62850     valueText : 'Value',
62851     
62852     dateFormat : 'm/j/Y',
62853     
62854     
62855     renderDate : function(dateVal){
62856         return dateVal.dateFormat(this.dateFormat);
62857     },
62858
62859     renderBool : function(bVal){
62860         return bVal ? 'true' : 'false';
62861     },
62862
62863     isCellEditable : function(colIndex, rowIndex){
62864         return colIndex == 1;
62865     },
62866
62867     getRenderer : function(col){
62868         return col == 1 ?
62869             this.renderCellDelegate : this.renderPropDelegate;
62870     },
62871
62872     renderProp : function(v){
62873         return this.getPropertyName(v);
62874     },
62875
62876     renderCell : function(val){
62877         var rv = val;
62878         if(val instanceof Date){
62879             rv = this.renderDate(val);
62880         }else if(typeof val == 'boolean'){
62881             rv = this.renderBool(val);
62882         }
62883         return Roo.util.Format.htmlEncode(rv);
62884     },
62885
62886     getPropertyName : function(name){
62887         var pn = this.grid.propertyNames;
62888         return pn && pn[name] ? pn[name] : name;
62889     },
62890
62891     getCellEditor : function(colIndex, rowIndex){
62892         var p = this.store.getProperty(rowIndex);
62893         var n = p.data['name'], val = p.data['value'];
62894         
62895         if(typeof(this.grid.customEditors[n]) == 'string'){
62896             return this.editors[this.grid.customEditors[n]];
62897         }
62898         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62899             return this.grid.customEditors[n];
62900         }
62901         if(val instanceof Date){
62902             return this.editors['date'];
62903         }else if(typeof val == 'number'){
62904             return this.editors['number'];
62905         }else if(typeof val == 'boolean'){
62906             return this.editors['boolean'];
62907         }else{
62908             return this.editors['string'];
62909         }
62910     }
62911 });
62912
62913 /**
62914  * @class Roo.grid.PropertyGrid
62915  * @extends Roo.grid.EditorGrid
62916  * This class represents the  interface of a component based property grid control.
62917  * <br><br>Usage:<pre><code>
62918  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62919       
62920  });
62921  // set any options
62922  grid.render();
62923  * </code></pre>
62924   
62925  * @constructor
62926  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62927  * The container MUST have some type of size defined for the grid to fill. The container will be
62928  * automatically set to position relative if it isn't already.
62929  * @param {Object} config A config object that sets properties on this grid.
62930  */
62931 Roo.grid.PropertyGrid = function(container, config){
62932     config = config || {};
62933     var store = new Roo.grid.PropertyStore(this);
62934     this.store = store;
62935     var cm = new Roo.grid.PropertyColumnModel(this, store);
62936     store.store.sort('name', 'ASC');
62937     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62938         ds: store.store,
62939         cm: cm,
62940         enableColLock:false,
62941         enableColumnMove:false,
62942         stripeRows:false,
62943         trackMouseOver: false,
62944         clicksToEdit:1
62945     }, config));
62946     this.getGridEl().addClass('x-props-grid');
62947     this.lastEditRow = null;
62948     this.on('columnresize', this.onColumnResize, this);
62949     this.addEvents({
62950          /**
62951              * @event beforepropertychange
62952              * Fires before a property changes (return false to stop?)
62953              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62954              * @param {String} id Record Id
62955              * @param {String} newval New Value
62956          * @param {String} oldval Old Value
62957              */
62958         "beforepropertychange": true,
62959         /**
62960              * @event propertychange
62961              * Fires after a property changes
62962              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62963              * @param {String} id Record Id
62964              * @param {String} newval New Value
62965          * @param {String} oldval Old Value
62966              */
62967         "propertychange": true
62968     });
62969     this.customEditors = this.customEditors || {};
62970 };
62971 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62972     
62973      /**
62974      * @cfg {Object} customEditors map of colnames=> custom editors.
62975      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62976      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62977      * false disables editing of the field.
62978          */
62979     
62980       /**
62981      * @cfg {Object} propertyNames map of property Names to their displayed value
62982          */
62983     
62984     render : function(){
62985         Roo.grid.PropertyGrid.superclass.render.call(this);
62986         this.autoSize.defer(100, this);
62987     },
62988
62989     autoSize : function(){
62990         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62991         if(this.view){
62992             this.view.fitColumns();
62993         }
62994     },
62995
62996     onColumnResize : function(){
62997         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62998         this.autoSize();
62999     },
63000     /**
63001      * Sets the data for the Grid
63002      * accepts a Key => Value object of all the elements avaiable.
63003      * @param {Object} data  to appear in grid.
63004      */
63005     setSource : function(source){
63006         this.store.setSource(source);
63007         //this.autoSize();
63008     },
63009     /**
63010      * Gets all the data from the grid.
63011      * @return {Object} data  data stored in grid
63012      */
63013     getSource : function(){
63014         return this.store.getSource();
63015     }
63016 });/*
63017   
63018  * Licence LGPL
63019  
63020  */
63021  
63022 /**
63023  * @class Roo.grid.Calendar
63024  * @extends Roo.grid.Grid
63025  * This class extends the Grid to provide a calendar widget
63026  * <br><br>Usage:<pre><code>
63027  var grid = new Roo.grid.Calendar("my-container-id", {
63028      ds: myDataStore,
63029      cm: myColModel,
63030      selModel: mySelectionModel,
63031      autoSizeColumns: true,
63032      monitorWindowResize: false,
63033      trackMouseOver: true
63034      eventstore : real data store..
63035  });
63036  // set any options
63037  grid.render();
63038   
63039   * @constructor
63040  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63041  * The container MUST have some type of size defined for the grid to fill. The container will be
63042  * automatically set to position relative if it isn't already.
63043  * @param {Object} config A config object that sets properties on this grid.
63044  */
63045 Roo.grid.Calendar = function(container, config){
63046         // initialize the container
63047         this.container = Roo.get(container);
63048         this.container.update("");
63049         this.container.setStyle("overflow", "hidden");
63050     this.container.addClass('x-grid-container');
63051
63052     this.id = this.container.id;
63053
63054     Roo.apply(this, config);
63055     // check and correct shorthanded configs
63056     
63057     var rows = [];
63058     var d =1;
63059     for (var r = 0;r < 6;r++) {
63060         
63061         rows[r]=[];
63062         for (var c =0;c < 7;c++) {
63063             rows[r][c]= '';
63064         }
63065     }
63066     if (this.eventStore) {
63067         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63068         this.eventStore.on('load',this.onLoad, this);
63069         this.eventStore.on('beforeload',this.clearEvents, this);
63070          
63071     }
63072     
63073     this.dataSource = new Roo.data.Store({
63074             proxy: new Roo.data.MemoryProxy(rows),
63075             reader: new Roo.data.ArrayReader({}, [
63076                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63077     });
63078
63079     this.dataSource.load();
63080     this.ds = this.dataSource;
63081     this.ds.xmodule = this.xmodule || false;
63082     
63083     
63084     var cellRender = function(v,x,r)
63085     {
63086         return String.format(
63087             '<div class="fc-day  fc-widget-content"><div>' +
63088                 '<div class="fc-event-container"></div>' +
63089                 '<div class="fc-day-number">{0}</div>'+
63090                 
63091                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63092             '</div></div>', v);
63093     
63094     }
63095     
63096     
63097     this.colModel = new Roo.grid.ColumnModel( [
63098         {
63099             xtype: 'ColumnModel',
63100             xns: Roo.grid,
63101             dataIndex : 'weekday0',
63102             header : 'Sunday',
63103             renderer : cellRender
63104         },
63105         {
63106             xtype: 'ColumnModel',
63107             xns: Roo.grid,
63108             dataIndex : 'weekday1',
63109             header : 'Monday',
63110             renderer : cellRender
63111         },
63112         {
63113             xtype: 'ColumnModel',
63114             xns: Roo.grid,
63115             dataIndex : 'weekday2',
63116             header : 'Tuesday',
63117             renderer : cellRender
63118         },
63119         {
63120             xtype: 'ColumnModel',
63121             xns: Roo.grid,
63122             dataIndex : 'weekday3',
63123             header : 'Wednesday',
63124             renderer : cellRender
63125         },
63126         {
63127             xtype: 'ColumnModel',
63128             xns: Roo.grid,
63129             dataIndex : 'weekday4',
63130             header : 'Thursday',
63131             renderer : cellRender
63132         },
63133         {
63134             xtype: 'ColumnModel',
63135             xns: Roo.grid,
63136             dataIndex : 'weekday5',
63137             header : 'Friday',
63138             renderer : cellRender
63139         },
63140         {
63141             xtype: 'ColumnModel',
63142             xns: Roo.grid,
63143             dataIndex : 'weekday6',
63144             header : 'Saturday',
63145             renderer : cellRender
63146         }
63147     ]);
63148     this.cm = this.colModel;
63149     this.cm.xmodule = this.xmodule || false;
63150  
63151         
63152           
63153     //this.selModel = new Roo.grid.CellSelectionModel();
63154     //this.sm = this.selModel;
63155     //this.selModel.init(this);
63156     
63157     
63158     if(this.width){
63159         this.container.setWidth(this.width);
63160     }
63161
63162     if(this.height){
63163         this.container.setHeight(this.height);
63164     }
63165     /** @private */
63166         this.addEvents({
63167         // raw events
63168         /**
63169          * @event click
63170          * The raw click event for the entire grid.
63171          * @param {Roo.EventObject} e
63172          */
63173         "click" : true,
63174         /**
63175          * @event dblclick
63176          * The raw dblclick event for the entire grid.
63177          * @param {Roo.EventObject} e
63178          */
63179         "dblclick" : true,
63180         /**
63181          * @event contextmenu
63182          * The raw contextmenu event for the entire grid.
63183          * @param {Roo.EventObject} e
63184          */
63185         "contextmenu" : true,
63186         /**
63187          * @event mousedown
63188          * The raw mousedown event for the entire grid.
63189          * @param {Roo.EventObject} e
63190          */
63191         "mousedown" : true,
63192         /**
63193          * @event mouseup
63194          * The raw mouseup event for the entire grid.
63195          * @param {Roo.EventObject} e
63196          */
63197         "mouseup" : true,
63198         /**
63199          * @event mouseover
63200          * The raw mouseover event for the entire grid.
63201          * @param {Roo.EventObject} e
63202          */
63203         "mouseover" : true,
63204         /**
63205          * @event mouseout
63206          * The raw mouseout event for the entire grid.
63207          * @param {Roo.EventObject} e
63208          */
63209         "mouseout" : true,
63210         /**
63211          * @event keypress
63212          * The raw keypress event for the entire grid.
63213          * @param {Roo.EventObject} e
63214          */
63215         "keypress" : true,
63216         /**
63217          * @event keydown
63218          * The raw keydown event for the entire grid.
63219          * @param {Roo.EventObject} e
63220          */
63221         "keydown" : true,
63222
63223         // custom events
63224
63225         /**
63226          * @event cellclick
63227          * Fires when a cell is clicked
63228          * @param {Grid} this
63229          * @param {Number} rowIndex
63230          * @param {Number} columnIndex
63231          * @param {Roo.EventObject} e
63232          */
63233         "cellclick" : true,
63234         /**
63235          * @event celldblclick
63236          * Fires when a cell is double clicked
63237          * @param {Grid} this
63238          * @param {Number} rowIndex
63239          * @param {Number} columnIndex
63240          * @param {Roo.EventObject} e
63241          */
63242         "celldblclick" : true,
63243         /**
63244          * @event rowclick
63245          * Fires when a row is clicked
63246          * @param {Grid} this
63247          * @param {Number} rowIndex
63248          * @param {Roo.EventObject} e
63249          */
63250         "rowclick" : true,
63251         /**
63252          * @event rowdblclick
63253          * Fires when a row is double clicked
63254          * @param {Grid} this
63255          * @param {Number} rowIndex
63256          * @param {Roo.EventObject} e
63257          */
63258         "rowdblclick" : true,
63259         /**
63260          * @event headerclick
63261          * Fires when a header is clicked
63262          * @param {Grid} this
63263          * @param {Number} columnIndex
63264          * @param {Roo.EventObject} e
63265          */
63266         "headerclick" : true,
63267         /**
63268          * @event headerdblclick
63269          * Fires when a header cell is double clicked
63270          * @param {Grid} this
63271          * @param {Number} columnIndex
63272          * @param {Roo.EventObject} e
63273          */
63274         "headerdblclick" : true,
63275         /**
63276          * @event rowcontextmenu
63277          * Fires when a row is right clicked
63278          * @param {Grid} this
63279          * @param {Number} rowIndex
63280          * @param {Roo.EventObject} e
63281          */
63282         "rowcontextmenu" : true,
63283         /**
63284          * @event cellcontextmenu
63285          * Fires when a cell is right clicked
63286          * @param {Grid} this
63287          * @param {Number} rowIndex
63288          * @param {Number} cellIndex
63289          * @param {Roo.EventObject} e
63290          */
63291          "cellcontextmenu" : true,
63292         /**
63293          * @event headercontextmenu
63294          * Fires when a header is right clicked
63295          * @param {Grid} this
63296          * @param {Number} columnIndex
63297          * @param {Roo.EventObject} e
63298          */
63299         "headercontextmenu" : true,
63300         /**
63301          * @event bodyscroll
63302          * Fires when the body element is scrolled
63303          * @param {Number} scrollLeft
63304          * @param {Number} scrollTop
63305          */
63306         "bodyscroll" : true,
63307         /**
63308          * @event columnresize
63309          * Fires when the user resizes a column
63310          * @param {Number} columnIndex
63311          * @param {Number} newSize
63312          */
63313         "columnresize" : true,
63314         /**
63315          * @event columnmove
63316          * Fires when the user moves a column
63317          * @param {Number} oldIndex
63318          * @param {Number} newIndex
63319          */
63320         "columnmove" : true,
63321         /**
63322          * @event startdrag
63323          * Fires when row(s) start being dragged
63324          * @param {Grid} this
63325          * @param {Roo.GridDD} dd The drag drop object
63326          * @param {event} e The raw browser event
63327          */
63328         "startdrag" : true,
63329         /**
63330          * @event enddrag
63331          * Fires when a drag operation is complete
63332          * @param {Grid} this
63333          * @param {Roo.GridDD} dd The drag drop object
63334          * @param {event} e The raw browser event
63335          */
63336         "enddrag" : true,
63337         /**
63338          * @event dragdrop
63339          * Fires when dragged row(s) are dropped on a valid DD target
63340          * @param {Grid} this
63341          * @param {Roo.GridDD} dd The drag drop object
63342          * @param {String} targetId The target drag drop object
63343          * @param {event} e The raw browser event
63344          */
63345         "dragdrop" : true,
63346         /**
63347          * @event dragover
63348          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63349          * @param {Grid} this
63350          * @param {Roo.GridDD} dd The drag drop object
63351          * @param {String} targetId The target drag drop object
63352          * @param {event} e The raw browser event
63353          */
63354         "dragover" : true,
63355         /**
63356          * @event dragenter
63357          *  Fires when the dragged row(s) first cross another DD target while being dragged
63358          * @param {Grid} this
63359          * @param {Roo.GridDD} dd The drag drop object
63360          * @param {String} targetId The target drag drop object
63361          * @param {event} e The raw browser event
63362          */
63363         "dragenter" : true,
63364         /**
63365          * @event dragout
63366          * Fires when the dragged row(s) leave another DD target while being dragged
63367          * @param {Grid} this
63368          * @param {Roo.GridDD} dd The drag drop object
63369          * @param {String} targetId The target drag drop object
63370          * @param {event} e The raw browser event
63371          */
63372         "dragout" : true,
63373         /**
63374          * @event rowclass
63375          * Fires when a row is rendered, so you can change add a style to it.
63376          * @param {GridView} gridview   The grid view
63377          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63378          */
63379         'rowclass' : true,
63380
63381         /**
63382          * @event render
63383          * Fires when the grid is rendered
63384          * @param {Grid} grid
63385          */
63386         'render' : true,
63387             /**
63388              * @event select
63389              * Fires when a date is selected
63390              * @param {DatePicker} this
63391              * @param {Date} date The selected date
63392              */
63393         'select': true,
63394         /**
63395              * @event monthchange
63396              * Fires when the displayed month changes 
63397              * @param {DatePicker} this
63398              * @param {Date} date The selected month
63399              */
63400         'monthchange': true,
63401         /**
63402              * @event evententer
63403              * Fires when mouse over an event
63404              * @param {Calendar} this
63405              * @param {event} Event
63406              */
63407         'evententer': true,
63408         /**
63409              * @event eventleave
63410              * Fires when the mouse leaves an
63411              * @param {Calendar} this
63412              * @param {event}
63413              */
63414         'eventleave': true,
63415         /**
63416              * @event eventclick
63417              * Fires when the mouse click an
63418              * @param {Calendar} this
63419              * @param {event}
63420              */
63421         'eventclick': true,
63422         /**
63423              * @event eventrender
63424              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63425              * @param {Calendar} this
63426              * @param {data} data to be modified
63427              */
63428         'eventrender': true
63429         
63430     });
63431
63432     Roo.grid.Grid.superclass.constructor.call(this);
63433     this.on('render', function() {
63434         this.view.el.addClass('x-grid-cal'); 
63435         
63436         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63437
63438     },this);
63439     
63440     if (!Roo.grid.Calendar.style) {
63441         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63442             
63443             
63444             '.x-grid-cal .x-grid-col' :  {
63445                 height: 'auto !important',
63446                 'vertical-align': 'top'
63447             },
63448             '.x-grid-cal  .fc-event-hori' : {
63449                 height: '14px'
63450             }
63451              
63452             
63453         }, Roo.id());
63454     }
63455
63456     
63457     
63458 };
63459 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63460     /**
63461      * @cfg {Store} eventStore The store that loads events.
63462      */
63463     eventStore : 25,
63464
63465      
63466     activeDate : false,
63467     startDay : 0,
63468     autoWidth : true,
63469     monitorWindowResize : false,
63470
63471     
63472     resizeColumns : function() {
63473         var col = (this.view.el.getWidth() / 7) - 3;
63474         // loop through cols, and setWidth
63475         for(var i =0 ; i < 7 ; i++){
63476             this.cm.setColumnWidth(i, col);
63477         }
63478     },
63479      setDate :function(date) {
63480         
63481         Roo.log('setDate?');
63482         
63483         this.resizeColumns();
63484         var vd = this.activeDate;
63485         this.activeDate = date;
63486 //        if(vd && this.el){
63487 //            var t = date.getTime();
63488 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63489 //                Roo.log('using add remove');
63490 //                
63491 //                this.fireEvent('monthchange', this, date);
63492 //                
63493 //                this.cells.removeClass("fc-state-highlight");
63494 //                this.cells.each(function(c){
63495 //                   if(c.dateValue == t){
63496 //                       c.addClass("fc-state-highlight");
63497 //                       setTimeout(function(){
63498 //                            try{c.dom.firstChild.focus();}catch(e){}
63499 //                       }, 50);
63500 //                       return false;
63501 //                   }
63502 //                   return true;
63503 //                });
63504 //                return;
63505 //            }
63506 //        }
63507         
63508         var days = date.getDaysInMonth();
63509         
63510         var firstOfMonth = date.getFirstDateOfMonth();
63511         var startingPos = firstOfMonth.getDay()-this.startDay;
63512         
63513         if(startingPos < this.startDay){
63514             startingPos += 7;
63515         }
63516         
63517         var pm = date.add(Date.MONTH, -1);
63518         var prevStart = pm.getDaysInMonth()-startingPos;
63519 //        
63520         
63521         
63522         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63523         
63524         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63525         //this.cells.addClassOnOver('fc-state-hover');
63526         
63527         var cells = this.cells.elements;
63528         var textEls = this.textNodes;
63529         
63530         //Roo.each(cells, function(cell){
63531         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63532         //});
63533         
63534         days += startingPos;
63535
63536         // convert everything to numbers so it's fast
63537         var day = 86400000;
63538         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63539         //Roo.log(d);
63540         //Roo.log(pm);
63541         //Roo.log(prevStart);
63542         
63543         var today = new Date().clearTime().getTime();
63544         var sel = date.clearTime().getTime();
63545         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63546         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63547         var ddMatch = this.disabledDatesRE;
63548         var ddText = this.disabledDatesText;
63549         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63550         var ddaysText = this.disabledDaysText;
63551         var format = this.format;
63552         
63553         var setCellClass = function(cal, cell){
63554             
63555             //Roo.log('set Cell Class');
63556             cell.title = "";
63557             var t = d.getTime();
63558             
63559             //Roo.log(d);
63560             
63561             
63562             cell.dateValue = t;
63563             if(t == today){
63564                 cell.className += " fc-today";
63565                 cell.className += " fc-state-highlight";
63566                 cell.title = cal.todayText;
63567             }
63568             if(t == sel){
63569                 // disable highlight in other month..
63570                 cell.className += " fc-state-highlight";
63571                 
63572             }
63573             // disabling
63574             if(t < min) {
63575                 //cell.className = " fc-state-disabled";
63576                 cell.title = cal.minText;
63577                 return;
63578             }
63579             if(t > max) {
63580                 //cell.className = " fc-state-disabled";
63581                 cell.title = cal.maxText;
63582                 return;
63583             }
63584             if(ddays){
63585                 if(ddays.indexOf(d.getDay()) != -1){
63586                     // cell.title = ddaysText;
63587                    // cell.className = " fc-state-disabled";
63588                 }
63589             }
63590             if(ddMatch && format){
63591                 var fvalue = d.dateFormat(format);
63592                 if(ddMatch.test(fvalue)){
63593                     cell.title = ddText.replace("%0", fvalue);
63594                    cell.className = " fc-state-disabled";
63595                 }
63596             }
63597             
63598             if (!cell.initialClassName) {
63599                 cell.initialClassName = cell.dom.className;
63600             }
63601             
63602             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63603         };
63604
63605         var i = 0;
63606         
63607         for(; i < startingPos; i++) {
63608             cells[i].dayName =  (++prevStart);
63609             Roo.log(textEls[i]);
63610             d.setDate(d.getDate()+1);
63611             
63612             //cells[i].className = "fc-past fc-other-month";
63613             setCellClass(this, cells[i]);
63614         }
63615         
63616         var intDay = 0;
63617         
63618         for(; i < days; i++){
63619             intDay = i - startingPos + 1;
63620             cells[i].dayName =  (intDay);
63621             d.setDate(d.getDate()+1);
63622             
63623             cells[i].className = ''; // "x-date-active";
63624             setCellClass(this, cells[i]);
63625         }
63626         var extraDays = 0;
63627         
63628         for(; i < 42; i++) {
63629             //textEls[i].innerHTML = (++extraDays);
63630             
63631             d.setDate(d.getDate()+1);
63632             cells[i].dayName = (++extraDays);
63633             cells[i].className = "fc-future fc-other-month";
63634             setCellClass(this, cells[i]);
63635         }
63636         
63637         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63638         
63639         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63640         
63641         // this will cause all the cells to mis
63642         var rows= [];
63643         var i =0;
63644         for (var r = 0;r < 6;r++) {
63645             for (var c =0;c < 7;c++) {
63646                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63647             }    
63648         }
63649         
63650         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63651         for(i=0;i<cells.length;i++) {
63652             
63653             this.cells.elements[i].dayName = cells[i].dayName ;
63654             this.cells.elements[i].className = cells[i].className;
63655             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63656             this.cells.elements[i].title = cells[i].title ;
63657             this.cells.elements[i].dateValue = cells[i].dateValue ;
63658         }
63659         
63660         
63661         
63662         
63663         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63664         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63665         
63666         ////if(totalRows != 6){
63667             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63668            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63669        // }
63670         
63671         this.fireEvent('monthchange', this, date);
63672         
63673         
63674     },
63675  /**
63676      * Returns the grid's SelectionModel.
63677      * @return {SelectionModel}
63678      */
63679     getSelectionModel : function(){
63680         if(!this.selModel){
63681             this.selModel = new Roo.grid.CellSelectionModel();
63682         }
63683         return this.selModel;
63684     },
63685
63686     load: function() {
63687         this.eventStore.load()
63688         
63689         
63690         
63691     },
63692     
63693     findCell : function(dt) {
63694         dt = dt.clearTime().getTime();
63695         var ret = false;
63696         this.cells.each(function(c){
63697             //Roo.log("check " +c.dateValue + '?=' + dt);
63698             if(c.dateValue == dt){
63699                 ret = c;
63700                 return false;
63701             }
63702             return true;
63703         });
63704         
63705         return ret;
63706     },
63707     
63708     findCells : function(rec) {
63709         var s = rec.data.start_dt.clone().clearTime().getTime();
63710        // Roo.log(s);
63711         var e= rec.data.end_dt.clone().clearTime().getTime();
63712        // Roo.log(e);
63713         var ret = [];
63714         this.cells.each(function(c){
63715              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63716             
63717             if(c.dateValue > e){
63718                 return ;
63719             }
63720             if(c.dateValue < s){
63721                 return ;
63722             }
63723             ret.push(c);
63724         });
63725         
63726         return ret;    
63727     },
63728     
63729     findBestRow: function(cells)
63730     {
63731         var ret = 0;
63732         
63733         for (var i =0 ; i < cells.length;i++) {
63734             ret  = Math.max(cells[i].rows || 0,ret);
63735         }
63736         return ret;
63737         
63738     },
63739     
63740     
63741     addItem : function(rec)
63742     {
63743         // look for vertical location slot in
63744         var cells = this.findCells(rec);
63745         
63746         rec.row = this.findBestRow(cells);
63747         
63748         // work out the location.
63749         
63750         var crow = false;
63751         var rows = [];
63752         for(var i =0; i < cells.length; i++) {
63753             if (!crow) {
63754                 crow = {
63755                     start : cells[i],
63756                     end :  cells[i]
63757                 };
63758                 continue;
63759             }
63760             if (crow.start.getY() == cells[i].getY()) {
63761                 // on same row.
63762                 crow.end = cells[i];
63763                 continue;
63764             }
63765             // different row.
63766             rows.push(crow);
63767             crow = {
63768                 start: cells[i],
63769                 end : cells[i]
63770             };
63771             
63772         }
63773         
63774         rows.push(crow);
63775         rec.els = [];
63776         rec.rows = rows;
63777         rec.cells = cells;
63778         for (var i = 0; i < cells.length;i++) {
63779             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63780             
63781         }
63782         
63783         
63784     },
63785     
63786     clearEvents: function() {
63787         
63788         if (!this.eventStore.getCount()) {
63789             return;
63790         }
63791         // reset number of rows in cells.
63792         Roo.each(this.cells.elements, function(c){
63793             c.rows = 0;
63794         });
63795         
63796         this.eventStore.each(function(e) {
63797             this.clearEvent(e);
63798         },this);
63799         
63800     },
63801     
63802     clearEvent : function(ev)
63803     {
63804         if (ev.els) {
63805             Roo.each(ev.els, function(el) {
63806                 el.un('mouseenter' ,this.onEventEnter, this);
63807                 el.un('mouseleave' ,this.onEventLeave, this);
63808                 el.remove();
63809             },this);
63810             ev.els = [];
63811         }
63812     },
63813     
63814     
63815     renderEvent : function(ev,ctr) {
63816         if (!ctr) {
63817              ctr = this.view.el.select('.fc-event-container',true).first();
63818         }
63819         
63820          
63821         this.clearEvent(ev);
63822             //code
63823        
63824         
63825         
63826         ev.els = [];
63827         var cells = ev.cells;
63828         var rows = ev.rows;
63829         this.fireEvent('eventrender', this, ev);
63830         
63831         for(var i =0; i < rows.length; i++) {
63832             
63833             cls = '';
63834             if (i == 0) {
63835                 cls += ' fc-event-start';
63836             }
63837             if ((i+1) == rows.length) {
63838                 cls += ' fc-event-end';
63839             }
63840             
63841             //Roo.log(ev.data);
63842             // how many rows should it span..
63843             var cg = this.eventTmpl.append(ctr,Roo.apply({
63844                 fccls : cls
63845                 
63846             }, ev.data) , true);
63847             
63848             
63849             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63850             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63851             cg.on('click', this.onEventClick, this, ev);
63852             
63853             ev.els.push(cg);
63854             
63855             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63856             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63857             //Roo.log(cg);
63858              
63859             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63860             cg.setWidth(ebox.right - sbox.x -2);
63861         }
63862     },
63863     
63864     renderEvents: function()
63865     {   
63866         // first make sure there is enough space..
63867         
63868         if (!this.eventTmpl) {
63869             this.eventTmpl = new Roo.Template(
63870                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63871                     '<div class="fc-event-inner">' +
63872                         '<span class="fc-event-time">{time}</span>' +
63873                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63874                     '</div>' +
63875                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63876                 '</div>'
63877             );
63878                 
63879         }
63880                
63881         
63882         
63883         this.cells.each(function(c) {
63884             //Roo.log(c.select('.fc-day-content div',true).first());
63885             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63886         });
63887         
63888         var ctr = this.view.el.select('.fc-event-container',true).first();
63889         
63890         var cls;
63891         this.eventStore.each(function(ev){
63892             
63893             this.renderEvent(ev);
63894              
63895              
63896         }, this);
63897         this.view.layout();
63898         
63899     },
63900     
63901     onEventEnter: function (e, el,event,d) {
63902         this.fireEvent('evententer', this, el, event);
63903     },
63904     
63905     onEventLeave: function (e, el,event,d) {
63906         this.fireEvent('eventleave', this, el, event);
63907     },
63908     
63909     onEventClick: function (e, el,event,d) {
63910         this.fireEvent('eventclick', this, el, event);
63911     },
63912     
63913     onMonthChange: function () {
63914         this.store.load();
63915     },
63916     
63917     onLoad: function () {
63918         
63919         //Roo.log('calendar onload');
63920 //         
63921         if(this.eventStore.getCount() > 0){
63922             
63923            
63924             
63925             this.eventStore.each(function(d){
63926                 
63927                 
63928                 // FIXME..
63929                 var add =   d.data;
63930                 if (typeof(add.end_dt) == 'undefined')  {
63931                     Roo.log("Missing End time in calendar data: ");
63932                     Roo.log(d);
63933                     return;
63934                 }
63935                 if (typeof(add.start_dt) == 'undefined')  {
63936                     Roo.log("Missing Start time in calendar data: ");
63937                     Roo.log(d);
63938                     return;
63939                 }
63940                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63941                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63942                 add.id = add.id || d.id;
63943                 add.title = add.title || '??';
63944                 
63945                 this.addItem(d);
63946                 
63947              
63948             },this);
63949         }
63950         
63951         this.renderEvents();
63952     }
63953     
63954
63955 });
63956 /*
63957  grid : {
63958                 xtype: 'Grid',
63959                 xns: Roo.grid,
63960                 listeners : {
63961                     render : function ()
63962                     {
63963                         _this.grid = this;
63964                         
63965                         if (!this.view.el.hasClass('course-timesheet')) {
63966                             this.view.el.addClass('course-timesheet');
63967                         }
63968                         if (this.tsStyle) {
63969                             this.ds.load({});
63970                             return; 
63971                         }
63972                         Roo.log('width');
63973                         Roo.log(_this.grid.view.el.getWidth());
63974                         
63975                         
63976                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63977                             '.course-timesheet .x-grid-row' : {
63978                                 height: '80px'
63979                             },
63980                             '.x-grid-row td' : {
63981                                 'vertical-align' : 0
63982                             },
63983                             '.course-edit-link' : {
63984                                 'color' : 'blue',
63985                                 'text-overflow' : 'ellipsis',
63986                                 'overflow' : 'hidden',
63987                                 'white-space' : 'nowrap',
63988                                 'cursor' : 'pointer'
63989                             },
63990                             '.sub-link' : {
63991                                 'color' : 'green'
63992                             },
63993                             '.de-act-sup-link' : {
63994                                 'color' : 'purple',
63995                                 'text-decoration' : 'line-through'
63996                             },
63997                             '.de-act-link' : {
63998                                 'color' : 'red',
63999                                 'text-decoration' : 'line-through'
64000                             },
64001                             '.course-timesheet .course-highlight' : {
64002                                 'border-top-style': 'dashed !important',
64003                                 'border-bottom-bottom': 'dashed !important'
64004                             },
64005                             '.course-timesheet .course-item' : {
64006                                 'font-family'   : 'tahoma, arial, helvetica',
64007                                 'font-size'     : '11px',
64008                                 'overflow'      : 'hidden',
64009                                 'padding-left'  : '10px',
64010                                 'padding-right' : '10px',
64011                                 'padding-top' : '10px' 
64012                             }
64013                             
64014                         }, Roo.id());
64015                                 this.ds.load({});
64016                     }
64017                 },
64018                 autoWidth : true,
64019                 monitorWindowResize : false,
64020                 cellrenderer : function(v,x,r)
64021                 {
64022                     return v;
64023                 },
64024                 sm : {
64025                     xtype: 'CellSelectionModel',
64026                     xns: Roo.grid
64027                 },
64028                 dataSource : {
64029                     xtype: 'Store',
64030                     xns: Roo.data,
64031                     listeners : {
64032                         beforeload : function (_self, options)
64033                         {
64034                             options.params = options.params || {};
64035                             options.params._month = _this.monthField.getValue();
64036                             options.params.limit = 9999;
64037                             options.params['sort'] = 'when_dt';    
64038                             options.params['dir'] = 'ASC';    
64039                             this.proxy.loadResponse = this.loadResponse;
64040                             Roo.log("load?");
64041                             //this.addColumns();
64042                         },
64043                         load : function (_self, records, options)
64044                         {
64045                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64046                                 // if you click on the translation.. you can edit it...
64047                                 var el = Roo.get(this);
64048                                 var id = el.dom.getAttribute('data-id');
64049                                 var d = el.dom.getAttribute('data-date');
64050                                 var t = el.dom.getAttribute('data-time');
64051                                 //var id = this.child('span').dom.textContent;
64052                                 
64053                                 //Roo.log(this);
64054                                 Pman.Dialog.CourseCalendar.show({
64055                                     id : id,
64056                                     when_d : d,
64057                                     when_t : t,
64058                                     productitem_active : id ? 1 : 0
64059                                 }, function() {
64060                                     _this.grid.ds.load({});
64061                                 });
64062                            
64063                            });
64064                            
64065                            _this.panel.fireEvent('resize', [ '', '' ]);
64066                         }
64067                     },
64068                     loadResponse : function(o, success, response){
64069                             // this is overridden on before load..
64070                             
64071                             Roo.log("our code?");       
64072                             //Roo.log(success);
64073                             //Roo.log(response)
64074                             delete this.activeRequest;
64075                             if(!success){
64076                                 this.fireEvent("loadexception", this, o, response);
64077                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64078                                 return;
64079                             }
64080                             var result;
64081                             try {
64082                                 result = o.reader.read(response);
64083                             }catch(e){
64084                                 Roo.log("load exception?");
64085                                 this.fireEvent("loadexception", this, o, response, e);
64086                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64087                                 return;
64088                             }
64089                             Roo.log("ready...");        
64090                             // loop through result.records;
64091                             // and set this.tdate[date] = [] << array of records..
64092                             _this.tdata  = {};
64093                             Roo.each(result.records, function(r){
64094                                 //Roo.log(r.data);
64095                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64096                                     _this.tdata[r.data.when_dt.format('j')] = [];
64097                                 }
64098                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64099                             });
64100                             
64101                             //Roo.log(_this.tdata);
64102                             
64103                             result.records = [];
64104                             result.totalRecords = 6;
64105                     
64106                             // let's generate some duumy records for the rows.
64107                             //var st = _this.dateField.getValue();
64108                             
64109                             // work out monday..
64110                             //st = st.add(Date.DAY, -1 * st.format('w'));
64111                             
64112                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64113                             
64114                             var firstOfMonth = date.getFirstDayOfMonth();
64115                             var days = date.getDaysInMonth();
64116                             var d = 1;
64117                             var firstAdded = false;
64118                             for (var i = 0; i < result.totalRecords ; i++) {
64119                                 //var d= st.add(Date.DAY, i);
64120                                 var row = {};
64121                                 var added = 0;
64122                                 for(var w = 0 ; w < 7 ; w++){
64123                                     if(!firstAdded && firstOfMonth != w){
64124                                         continue;
64125                                     }
64126                                     if(d > days){
64127                                         continue;
64128                                     }
64129                                     firstAdded = true;
64130                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64131                                     row['weekday'+w] = String.format(
64132                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64133                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64134                                                     d,
64135                                                     date.format('Y-m-')+dd
64136                                                 );
64137                                     added++;
64138                                     if(typeof(_this.tdata[d]) != 'undefined'){
64139                                         Roo.each(_this.tdata[d], function(r){
64140                                             var is_sub = '';
64141                                             var deactive = '';
64142                                             var id = r.id;
64143                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64144                                             if(r.parent_id*1>0){
64145                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64146                                                 id = r.parent_id;
64147                                             }
64148                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64149                                                 deactive = 'de-act-link';
64150                                             }
64151                                             
64152                                             row['weekday'+w] += String.format(
64153                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64154                                                     id, //0
64155                                                     r.product_id_name, //1
64156                                                     r.when_dt.format('h:ia'), //2
64157                                                     is_sub, //3
64158                                                     deactive, //4
64159                                                     desc // 5
64160                                             );
64161                                         });
64162                                     }
64163                                     d++;
64164                                 }
64165                                 
64166                                 // only do this if something added..
64167                                 if(added > 0){ 
64168                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64169                                 }
64170                                 
64171                                 
64172                                 // push it twice. (second one with an hour..
64173                                 
64174                             }
64175                             //Roo.log(result);
64176                             this.fireEvent("load", this, o, o.request.arg);
64177                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64178                         },
64179                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64180                     proxy : {
64181                         xtype: 'HttpProxy',
64182                         xns: Roo.data,
64183                         method : 'GET',
64184                         url : baseURL + '/Roo/Shop_course.php'
64185                     },
64186                     reader : {
64187                         xtype: 'JsonReader',
64188                         xns: Roo.data,
64189                         id : 'id',
64190                         fields : [
64191                             {
64192                                 'name': 'id',
64193                                 'type': 'int'
64194                             },
64195                             {
64196                                 'name': 'when_dt',
64197                                 'type': 'string'
64198                             },
64199                             {
64200                                 'name': 'end_dt',
64201                                 'type': 'string'
64202                             },
64203                             {
64204                                 'name': 'parent_id',
64205                                 'type': 'int'
64206                             },
64207                             {
64208                                 'name': 'product_id',
64209                                 'type': 'int'
64210                             },
64211                             {
64212                                 'name': 'productitem_id',
64213                                 'type': 'int'
64214                             },
64215                             {
64216                                 'name': 'guid',
64217                                 'type': 'int'
64218                             }
64219                         ]
64220                     }
64221                 },
64222                 toolbar : {
64223                     xtype: 'Toolbar',
64224                     xns: Roo,
64225                     items : [
64226                         {
64227                             xtype: 'Button',
64228                             xns: Roo.Toolbar,
64229                             listeners : {
64230                                 click : function (_self, e)
64231                                 {
64232                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64233                                     sd.setMonth(sd.getMonth()-1);
64234                                     _this.monthField.setValue(sd.format('Y-m-d'));
64235                                     _this.grid.ds.load({});
64236                                 }
64237                             },
64238                             text : "Back"
64239                         },
64240                         {
64241                             xtype: 'Separator',
64242                             xns: Roo.Toolbar
64243                         },
64244                         {
64245                             xtype: 'MonthField',
64246                             xns: Roo.form,
64247                             listeners : {
64248                                 render : function (_self)
64249                                 {
64250                                     _this.monthField = _self;
64251                                    // _this.monthField.set  today
64252                                 },
64253                                 select : function (combo, date)
64254                                 {
64255                                     _this.grid.ds.load({});
64256                                 }
64257                             },
64258                             value : (function() { return new Date(); })()
64259                         },
64260                         {
64261                             xtype: 'Separator',
64262                             xns: Roo.Toolbar
64263                         },
64264                         {
64265                             xtype: 'TextItem',
64266                             xns: Roo.Toolbar,
64267                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64268                         },
64269                         {
64270                             xtype: 'Fill',
64271                             xns: Roo.Toolbar
64272                         },
64273                         {
64274                             xtype: 'Button',
64275                             xns: Roo.Toolbar,
64276                             listeners : {
64277                                 click : function (_self, e)
64278                                 {
64279                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64280                                     sd.setMonth(sd.getMonth()+1);
64281                                     _this.monthField.setValue(sd.format('Y-m-d'));
64282                                     _this.grid.ds.load({});
64283                                 }
64284                             },
64285                             text : "Next"
64286                         }
64287                     ]
64288                 },
64289                  
64290             }
64291         };
64292         
64293         *//*
64294  * Based on:
64295  * Ext JS Library 1.1.1
64296  * Copyright(c) 2006-2007, Ext JS, LLC.
64297  *
64298  * Originally Released Under LGPL - original licence link has changed is not relivant.
64299  *
64300  * Fork - LGPL
64301  * <script type="text/javascript">
64302  */
64303  
64304 /**
64305  * @class Roo.LoadMask
64306  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64307  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64308  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64309  * element's UpdateManager load indicator and will be destroyed after the initial load.
64310  * @constructor
64311  * Create a new LoadMask
64312  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64313  * @param {Object} config The config object
64314  */
64315 Roo.LoadMask = function(el, config){
64316     this.el = Roo.get(el);
64317     Roo.apply(this, config);
64318     if(this.store){
64319         this.store.on('beforeload', this.onBeforeLoad, this);
64320         this.store.on('load', this.onLoad, this);
64321         this.store.on('loadexception', this.onLoadException, this);
64322         this.removeMask = false;
64323     }else{
64324         var um = this.el.getUpdateManager();
64325         um.showLoadIndicator = false; // disable the default indicator
64326         um.on('beforeupdate', this.onBeforeLoad, this);
64327         um.on('update', this.onLoad, this);
64328         um.on('failure', this.onLoad, this);
64329         this.removeMask = true;
64330     }
64331 };
64332
64333 Roo.LoadMask.prototype = {
64334     /**
64335      * @cfg {Boolean} removeMask
64336      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64337      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64338      */
64339     removeMask : false,
64340     /**
64341      * @cfg {String} msg
64342      * The text to display in a centered loading message box (defaults to 'Loading...')
64343      */
64344     msg : 'Loading...',
64345     /**
64346      * @cfg {String} msgCls
64347      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64348      */
64349     msgCls : 'x-mask-loading',
64350
64351     /**
64352      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64353      * @type Boolean
64354      */
64355     disabled: false,
64356
64357     /**
64358      * Disables the mask to prevent it from being displayed
64359      */
64360     disable : function(){
64361        this.disabled = true;
64362     },
64363
64364     /**
64365      * Enables the mask so that it can be displayed
64366      */
64367     enable : function(){
64368         this.disabled = false;
64369     },
64370     
64371     onLoadException : function()
64372     {
64373         Roo.log(arguments);
64374         
64375         if (typeof(arguments[3]) != 'undefined') {
64376             Roo.MessageBox.alert("Error loading",arguments[3]);
64377         } 
64378         /*
64379         try {
64380             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64381                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64382             }   
64383         } catch(e) {
64384             
64385         }
64386         */
64387     
64388         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64389     },
64390     // private
64391     onLoad : function()
64392     {
64393         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64394     },
64395
64396     // private
64397     onBeforeLoad : function(){
64398         if(!this.disabled){
64399             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64400         }
64401     },
64402
64403     // private
64404     destroy : function(){
64405         if(this.store){
64406             this.store.un('beforeload', this.onBeforeLoad, this);
64407             this.store.un('load', this.onLoad, this);
64408             this.store.un('loadexception', this.onLoadException, this);
64409         }else{
64410             var um = this.el.getUpdateManager();
64411             um.un('beforeupdate', this.onBeforeLoad, this);
64412             um.un('update', this.onLoad, this);
64413             um.un('failure', this.onLoad, this);
64414         }
64415     }
64416 };/*
64417  * Based on:
64418  * Ext JS Library 1.1.1
64419  * Copyright(c) 2006-2007, Ext JS, LLC.
64420  *
64421  * Originally Released Under LGPL - original licence link has changed is not relivant.
64422  *
64423  * Fork - LGPL
64424  * <script type="text/javascript">
64425  */
64426
64427
64428 /**
64429  * @class Roo.XTemplate
64430  * @extends Roo.Template
64431  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64432 <pre><code>
64433 var t = new Roo.XTemplate(
64434         '&lt;select name="{name}"&gt;',
64435                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64436         '&lt;/select&gt;'
64437 );
64438  
64439 // then append, applying the master template values
64440  </code></pre>
64441  *
64442  * Supported features:
64443  *
64444  *  Tags:
64445
64446 <pre><code>
64447       {a_variable} - output encoded.
64448       {a_variable.format:("Y-m-d")} - call a method on the variable
64449       {a_variable:raw} - unencoded output
64450       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64451       {a_variable:this.method_on_template(...)} - call a method on the template object.
64452  
64453 </code></pre>
64454  *  The tpl tag:
64455 <pre><code>
64456         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64457         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64458         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64459         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64460   
64461         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64462         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64463 </code></pre>
64464  *      
64465  */
64466 Roo.XTemplate = function()
64467 {
64468     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64469     if (this.html) {
64470         this.compile();
64471     }
64472 };
64473
64474
64475 Roo.extend(Roo.XTemplate, Roo.Template, {
64476
64477     /**
64478      * The various sub templates
64479      */
64480     tpls : false,
64481     /**
64482      *
64483      * basic tag replacing syntax
64484      * WORD:WORD()
64485      *
64486      * // you can fake an object call by doing this
64487      *  x.t:(test,tesT) 
64488      * 
64489      */
64490     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64491
64492     /**
64493      * compile the template
64494      *
64495      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64496      *
64497      */
64498     compile: function()
64499     {
64500         var s = this.html;
64501      
64502         s = ['<tpl>', s, '</tpl>'].join('');
64503     
64504         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64505             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64506             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64507             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64508             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64509             m,
64510             id     = 0,
64511             tpls   = [];
64512     
64513         while(true == !!(m = s.match(re))){
64514             var forMatch   = m[0].match(nameRe),
64515                 ifMatch   = m[0].match(ifRe),
64516                 execMatch   = m[0].match(execRe),
64517                 namedMatch   = m[0].match(namedRe),
64518                 
64519                 exp  = null, 
64520                 fn   = null,
64521                 exec = null,
64522                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64523                 
64524             if (ifMatch) {
64525                 // if - puts fn into test..
64526                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64527                 if(exp){
64528                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64529                 }
64530             }
64531             
64532             if (execMatch) {
64533                 // exec - calls a function... returns empty if true is  returned.
64534                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64535                 if(exp){
64536                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64537                 }
64538             }
64539             
64540             
64541             if (name) {
64542                 // for = 
64543                 switch(name){
64544                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64545                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64546                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64547                 }
64548             }
64549             var uid = namedMatch ? namedMatch[1] : id;
64550             
64551             
64552             tpls.push({
64553                 id:     namedMatch ? namedMatch[1] : id,
64554                 target: name,
64555                 exec:   exec,
64556                 test:   fn,
64557                 body:   m[1] || ''
64558             });
64559             if (namedMatch) {
64560                 s = s.replace(m[0], '');
64561             } else { 
64562                 s = s.replace(m[0], '{xtpl'+ id + '}');
64563             }
64564             ++id;
64565         }
64566         this.tpls = [];
64567         for(var i = tpls.length-1; i >= 0; --i){
64568             this.compileTpl(tpls[i]);
64569             this.tpls[tpls[i].id] = tpls[i];
64570         }
64571         this.master = tpls[tpls.length-1];
64572         return this;
64573     },
64574     /**
64575      * same as applyTemplate, except it's done to one of the subTemplates
64576      * when using named templates, you can do:
64577      *
64578      * var str = pl.applySubTemplate('your-name', values);
64579      *
64580      * 
64581      * @param {Number} id of the template
64582      * @param {Object} values to apply to template
64583      * @param {Object} parent (normaly the instance of this object)
64584      */
64585     applySubTemplate : function(id, values, parent)
64586     {
64587         
64588         
64589         var t = this.tpls[id];
64590         
64591         
64592         try { 
64593             if(t.test && !t.test.call(this, values, parent)){
64594                 return '';
64595             }
64596         } catch(e) {
64597             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64598             Roo.log(e.toString());
64599             Roo.log(t.test);
64600             return ''
64601         }
64602         try { 
64603             
64604             if(t.exec && t.exec.call(this, values, parent)){
64605                 return '';
64606             }
64607         } catch(e) {
64608             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64609             Roo.log(e.toString());
64610             Roo.log(t.exec);
64611             return ''
64612         }
64613         try {
64614             var vs = t.target ? t.target.call(this, values, parent) : values;
64615             parent = t.target ? values : parent;
64616             if(t.target && vs instanceof Array){
64617                 var buf = [];
64618                 for(var i = 0, len = vs.length; i < len; i++){
64619                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64620                 }
64621                 return buf.join('');
64622             }
64623             return t.compiled.call(this, vs, parent);
64624         } catch (e) {
64625             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64626             Roo.log(e.toString());
64627             Roo.log(t.compiled);
64628             return '';
64629         }
64630     },
64631
64632     compileTpl : function(tpl)
64633     {
64634         var fm = Roo.util.Format;
64635         var useF = this.disableFormats !== true;
64636         var sep = Roo.isGecko ? "+" : ",";
64637         var undef = function(str) {
64638             Roo.log("Property not found :"  + str);
64639             return '';
64640         };
64641         
64642         var fn = function(m, name, format, args)
64643         {
64644             //Roo.log(arguments);
64645             args = args ? args.replace(/\\'/g,"'") : args;
64646             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64647             if (typeof(format) == 'undefined') {
64648                 format= 'htmlEncode';
64649             }
64650             if (format == 'raw' ) {
64651                 format = false;
64652             }
64653             
64654             if(name.substr(0, 4) == 'xtpl'){
64655                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64656             }
64657             
64658             // build an array of options to determine if value is undefined..
64659             
64660             // basically get 'xxxx.yyyy' then do
64661             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64662             //    (function () { Roo.log("Property not found"); return ''; })() :
64663             //    ......
64664             
64665             var udef_ar = [];
64666             var lookfor = '';
64667             Roo.each(name.split('.'), function(st) {
64668                 lookfor += (lookfor.length ? '.': '') + st;
64669                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64670             });
64671             
64672             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64673             
64674             
64675             if(format && useF){
64676                 
64677                 args = args ? ',' + args : "";
64678                  
64679                 if(format.substr(0, 5) != "this."){
64680                     format = "fm." + format + '(';
64681                 }else{
64682                     format = 'this.call("'+ format.substr(5) + '", ';
64683                     args = ", values";
64684                 }
64685                 
64686                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64687             }
64688              
64689             if (args.length) {
64690                 // called with xxyx.yuu:(test,test)
64691                 // change to ()
64692                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64693             }
64694             // raw.. - :raw modifier..
64695             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64696             
64697         };
64698         var body;
64699         // branched to use + in gecko and [].join() in others
64700         if(Roo.isGecko){
64701             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64702                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64703                     "';};};";
64704         }else{
64705             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64706             body.push(tpl.body.replace(/(\r\n|\n)/g,
64707                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64708             body.push("'].join('');};};");
64709             body = body.join('');
64710         }
64711         
64712         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64713        
64714         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64715         eval(body);
64716         
64717         return this;
64718     },
64719
64720     applyTemplate : function(values){
64721         return this.master.compiled.call(this, values, {});
64722         //var s = this.subs;
64723     },
64724
64725     apply : function(){
64726         return this.applyTemplate.apply(this, arguments);
64727     }
64728
64729  });
64730
64731 Roo.XTemplate.from = function(el){
64732     el = Roo.getDom(el);
64733     return new Roo.XTemplate(el.value || el.innerHTML);
64734 };