roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y); v.setFullYear(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /*
4901  * Based on:
4902  * Ext JS Library 1.1.1
4903  * Copyright(c) 2006-2007, Ext JS, LLC.
4904  *
4905  * Originally Released Under LGPL - original licence link has changed is not relivant.
4906  *
4907  * Fork - LGPL
4908  * <script type="text/javascript">
4909  */
4910
4911
4912 // nasty IE9 hack - what a pile of crap that is..
4913
4914  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4915     Range.prototype.createContextualFragment = function (html) {
4916         var doc = window.document;
4917         var container = doc.createElement("div");
4918         container.innerHTML = html;
4919         var frag = doc.createDocumentFragment(), n;
4920         while ((n = container.firstChild)) {
4921             frag.appendChild(n);
4922         }
4923         return frag;
4924     };
4925 }
4926
4927 /**
4928  * @class Roo.DomHelper
4929  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4930  * 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>.
4931  * @static
4932  */
4933 Roo.DomHelper = function(){
4934     var tempTableEl = null;
4935     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4936     var tableRe = /^table|tbody|tr|td$/i;
4937     var xmlns = {};
4938     // build as innerHTML where available
4939     /** @ignore */
4940     var createHtml = function(o){
4941         if(typeof o == 'string'){
4942             return o;
4943         }
4944         var b = "";
4945         if(!o.tag){
4946             o.tag = "div";
4947         }
4948         b += "<" + o.tag;
4949         for(var attr in o){
4950             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4951             if(attr == "style"){
4952                 var s = o["style"];
4953                 if(typeof s == "function"){
4954                     s = s.call();
4955                 }
4956                 if(typeof s == "string"){
4957                     b += ' style="' + s + '"';
4958                 }else if(typeof s == "object"){
4959                     b += ' style="';
4960                     for(var key in s){
4961                         if(typeof s[key] != "function"){
4962                             b += key + ":" + s[key] + ";";
4963                         }
4964                     }
4965                     b += '"';
4966                 }
4967             }else{
4968                 if(attr == "cls"){
4969                     b += ' class="' + o["cls"] + '"';
4970                 }else if(attr == "htmlFor"){
4971                     b += ' for="' + o["htmlFor"] + '"';
4972                 }else{
4973                     b += " " + attr + '="' + o[attr] + '"';
4974                 }
4975             }
4976         }
4977         if(emptyTags.test(o.tag)){
4978             b += "/>";
4979         }else{
4980             b += ">";
4981             var cn = o.children || o.cn;
4982             if(cn){
4983                 //http://bugs.kde.org/show_bug.cgi?id=71506
4984                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4985                     for(var i = 0, len = cn.length; i < len; i++) {
4986                         b += createHtml(cn[i], b);
4987                     }
4988                 }else{
4989                     b += createHtml(cn, b);
4990                 }
4991             }
4992             if(o.html){
4993                 b += o.html;
4994             }
4995             b += "</" + o.tag + ">";
4996         }
4997         return b;
4998     };
4999
5000     // build as dom
5001     /** @ignore */
5002     var createDom = function(o, parentNode){
5003          
5004         // defininition craeted..
5005         var ns = false;
5006         if (o.ns && o.ns != 'html') {
5007                
5008             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5009                 xmlns[o.ns] = o.xmlns;
5010                 ns = o.xmlns;
5011             }
5012             if (typeof(xmlns[o.ns]) == 'undefined') {
5013                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5014             }
5015             ns = xmlns[o.ns];
5016         }
5017         
5018         
5019         if (typeof(o) == 'string') {
5020             return parentNode.appendChild(document.createTextNode(o));
5021         }
5022         o.tag = o.tag || div;
5023         if (o.ns && Roo.isIE) {
5024             ns = false;
5025             o.tag = o.ns + ':' + o.tag;
5026             
5027         }
5028         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5029         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5030         for(var attr in o){
5031             
5032             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5033                     attr == "style" || typeof o[attr] == "function") { continue; }
5034                     
5035             if(attr=="cls" && Roo.isIE){
5036                 el.className = o["cls"];
5037             }else{
5038                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5039                 else { 
5040                     el[attr] = o[attr];
5041                 }
5042             }
5043         }
5044         Roo.DomHelper.applyStyles(el, o.style);
5045         var cn = o.children || o.cn;
5046         if(cn){
5047             //http://bugs.kde.org/show_bug.cgi?id=71506
5048              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5049                 for(var i = 0, len = cn.length; i < len; i++) {
5050                     createDom(cn[i], el);
5051                 }
5052             }else{
5053                 createDom(cn, el);
5054             }
5055         }
5056         if(o.html){
5057             el.innerHTML = o.html;
5058         }
5059         if(parentNode){
5060            parentNode.appendChild(el);
5061         }
5062         return el;
5063     };
5064
5065     var ieTable = function(depth, s, h, e){
5066         tempTableEl.innerHTML = [s, h, e].join('');
5067         var i = -1, el = tempTableEl;
5068         while(++i < depth && el.firstChild){
5069             el = el.firstChild;
5070         }
5071         return el;
5072     };
5073
5074     // kill repeat to save bytes
5075     var ts = '<table>',
5076         te = '</table>',
5077         tbs = ts+'<tbody>',
5078         tbe = '</tbody>'+te,
5079         trs = tbs + '<tr>',
5080         tre = '</tr>'+tbe;
5081
5082     /**
5083      * @ignore
5084      * Nasty code for IE's broken table implementation
5085      */
5086     var insertIntoTable = function(tag, where, el, html){
5087         if(!tempTableEl){
5088             tempTableEl = document.createElement('div');
5089         }
5090         var node;
5091         var before = null;
5092         if(tag == 'td'){
5093             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5094                 return;
5095             }
5096             if(where == 'beforebegin'){
5097                 before = el;
5098                 el = el.parentNode;
5099             } else{
5100                 before = el.nextSibling;
5101                 el = el.parentNode;
5102             }
5103             node = ieTable(4, trs, html, tre);
5104         }
5105         else if(tag == 'tr'){
5106             if(where == 'beforebegin'){
5107                 before = el;
5108                 el = el.parentNode;
5109                 node = ieTable(3, tbs, html, tbe);
5110             } else if(where == 'afterend'){
5111                 before = el.nextSibling;
5112                 el = el.parentNode;
5113                 node = ieTable(3, tbs, html, tbe);
5114             } else{ // INTO a TR
5115                 if(where == 'afterbegin'){
5116                     before = el.firstChild;
5117                 }
5118                 node = ieTable(4, trs, html, tre);
5119             }
5120         } else if(tag == 'tbody'){
5121             if(where == 'beforebegin'){
5122                 before = el;
5123                 el = el.parentNode;
5124                 node = ieTable(2, ts, html, te);
5125             } else if(where == 'afterend'){
5126                 before = el.nextSibling;
5127                 el = el.parentNode;
5128                 node = ieTable(2, ts, html, te);
5129             } else{
5130                 if(where == 'afterbegin'){
5131                     before = el.firstChild;
5132                 }
5133                 node = ieTable(3, tbs, html, tbe);
5134             }
5135         } else{ // TABLE
5136             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5137                 return;
5138             }
5139             if(where == 'afterbegin'){
5140                 before = el.firstChild;
5141             }
5142             node = ieTable(2, ts, html, te);
5143         }
5144         el.insertBefore(node, before);
5145         return node;
5146     };
5147     
5148     // this is a bit like the react update code...
5149     // 
5150     
5151     var updateNode = function(from, to)
5152     {
5153         // should we handle non-standard elements?
5154         Roo.log(["UpdateNode" , from, to]);
5155         if (from.nodeType != to.nodeType) {
5156             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             Roo.log(["ReplaceChild" , from, to ]);
5172             from.parentNode.replaceChild(to, from);
5173             return;
5174         }
5175         // compare attributes
5176         var ar = Array.from(from.attributes);
5177         for(var i = 0; i< ar.length;i++) {
5178             if (to.hasAttribute(ar[i].name)) {
5179                 continue;
5180             }
5181             if (ar[i].name == 'id') { // always keep ids?
5182                 continue;
5183             }
5184             from.removeAttribute(ar[i].name);
5185         }
5186         ar = to.attributes;
5187         for(var i = 0; i< ar.length;i++) {
5188             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5189                 continue;
5190             }
5191             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5192         }
5193         // children
5194         var far = Array.from(from.childNodes);
5195         var tar = Array.from(to.childNodes);
5196         // if the lengths are different.. then it's probably a editable content change, rather than
5197         // a change of the block definition..
5198         
5199         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5200          /*if (from.innerHTML == to.innerHTML) {
5201             return;
5202         }
5203         if (far.length != tar.length) {
5204             from.innerHTML = to.innerHTML;
5205             return;
5206         }
5207         */
5208         
5209         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5210             if (i >= far.length) {
5211                 from.appendChild(tar[i]);
5212                 Roo.log(["add", tar[i]]);
5213                 
5214             } else if ( i  >= tar.length) {
5215                 from.removeChild(far[i]);
5216                 Roo.log(["remove", far[i]]);
5217             } else {
5218                 
5219                 updateNode(far[i], tar[i]);
5220             }    
5221         }
5222         
5223         
5224         
5225         
5226     };
5227     
5228     
5229
5230     return {
5231         /** True to force the use of DOM instead of html fragments @type Boolean */
5232         useDom : false,
5233     
5234         /**
5235          * Returns the markup for the passed Element(s) config
5236          * @param {Object} o The Dom object spec (and children)
5237          * @return {String}
5238          */
5239         markup : function(o){
5240             return createHtml(o);
5241         },
5242     
5243         /**
5244          * Applies a style specification to an element
5245          * @param {String/HTMLElement} el The element to apply styles to
5246          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5247          * a function which returns such a specification.
5248          */
5249         applyStyles : function(el, styles){
5250             if(styles){
5251                el = Roo.fly(el);
5252                if(typeof styles == "string"){
5253                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5254                    var matches;
5255                    while ((matches = re.exec(styles)) != null){
5256                        el.setStyle(matches[1], matches[2]);
5257                    }
5258                }else if (typeof styles == "object"){
5259                    for (var style in styles){
5260                       el.setStyle(style, styles[style]);
5261                    }
5262                }else if (typeof styles == "function"){
5263                     Roo.DomHelper.applyStyles(el, styles.call());
5264                }
5265             }
5266         },
5267     
5268         /**
5269          * Inserts an HTML fragment into the Dom
5270          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5271          * @param {HTMLElement} el The context element
5272          * @param {String} html The HTML fragmenet
5273          * @return {HTMLElement} The new node
5274          */
5275         insertHtml : function(where, el, html){
5276             where = where.toLowerCase();
5277             if(el.insertAdjacentHTML){
5278                 if(tableRe.test(el.tagName)){
5279                     var rs;
5280                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5281                         return rs;
5282                     }
5283                 }
5284                 switch(where){
5285                     case "beforebegin":
5286                         el.insertAdjacentHTML('BeforeBegin', html);
5287                         return el.previousSibling;
5288                     case "afterbegin":
5289                         el.insertAdjacentHTML('AfterBegin', html);
5290                         return el.firstChild;
5291                     case "beforeend":
5292                         el.insertAdjacentHTML('BeforeEnd', html);
5293                         return el.lastChild;
5294                     case "afterend":
5295                         el.insertAdjacentHTML('AfterEnd', html);
5296                         return el.nextSibling;
5297                 }
5298                 throw 'Illegal insertion point -> "' + where + '"';
5299             }
5300             var range = el.ownerDocument.createRange();
5301             var frag;
5302             switch(where){
5303                  case "beforebegin":
5304                     range.setStartBefore(el);
5305                     frag = range.createContextualFragment(html);
5306                     el.parentNode.insertBefore(frag, el);
5307                     return el.previousSibling;
5308                  case "afterbegin":
5309                     if(el.firstChild){
5310                         range.setStartBefore(el.firstChild);
5311                         frag = range.createContextualFragment(html);
5312                         el.insertBefore(frag, el.firstChild);
5313                         return el.firstChild;
5314                     }else{
5315                         el.innerHTML = html;
5316                         return el.firstChild;
5317                     }
5318                 case "beforeend":
5319                     if(el.lastChild){
5320                         range.setStartAfter(el.lastChild);
5321                         frag = range.createContextualFragment(html);
5322                         el.appendChild(frag);
5323                         return el.lastChild;
5324                     }else{
5325                         el.innerHTML = html;
5326                         return el.lastChild;
5327                     }
5328                 case "afterend":
5329                     range.setStartAfter(el);
5330                     frag = range.createContextualFragment(html);
5331                     el.parentNode.insertBefore(frag, el.nextSibling);
5332                     return el.nextSibling;
5333                 }
5334                 throw 'Illegal insertion point -> "' + where + '"';
5335         },
5336     
5337         /**
5338          * Creates new Dom element(s) and inserts them before el
5339          * @param {String/HTMLElement/Element} el The context element
5340          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5341          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5342          * @return {HTMLElement/Roo.Element} The new node
5343          */
5344         insertBefore : function(el, o, returnElement){
5345             return this.doInsert(el, o, returnElement, "beforeBegin");
5346         },
5347     
5348         /**
5349          * Creates new Dom element(s) and inserts them after el
5350          * @param {String/HTMLElement/Element} el The context element
5351          * @param {Object} o The Dom object spec (and children)
5352          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5353          * @return {HTMLElement/Roo.Element} The new node
5354          */
5355         insertAfter : function(el, o, returnElement){
5356             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5357         },
5358     
5359         /**
5360          * Creates new Dom element(s) and inserts them as the first child of el
5361          * @param {String/HTMLElement/Element} el The context element
5362          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5363          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5364          * @return {HTMLElement/Roo.Element} The new node
5365          */
5366         insertFirst : function(el, o, returnElement){
5367             return this.doInsert(el, o, returnElement, "afterBegin");
5368         },
5369     
5370         // private
5371         doInsert : function(el, o, returnElement, pos, sibling){
5372             el = Roo.getDom(el);
5373             var newNode;
5374             if(this.useDom || o.ns){
5375                 newNode = createDom(o, null);
5376                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5377             }else{
5378                 var html = createHtml(o);
5379                 newNode = this.insertHtml(pos, el, html);
5380             }
5381             return returnElement ? Roo.get(newNode, true) : newNode;
5382         },
5383     
5384         /**
5385          * Creates new Dom element(s) and appends them to el
5386          * @param {String/HTMLElement/Element} el The context element
5387          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5388          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5389          * @return {HTMLElement/Roo.Element} The new node
5390          */
5391         append : function(el, o, returnElement){
5392             el = Roo.getDom(el);
5393             var newNode;
5394             if(this.useDom || o.ns){
5395                 newNode = createDom(o, null);
5396                 el.appendChild(newNode);
5397             }else{
5398                 var html = createHtml(o);
5399                 newNode = this.insertHtml("beforeEnd", el, html);
5400             }
5401             return returnElement ? Roo.get(newNode, true) : newNode;
5402         },
5403     
5404         /**
5405          * Creates new Dom element(s) and overwrites the contents of el with them
5406          * @param {String/HTMLElement/Element} el The context element
5407          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5408          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5409          * @return {HTMLElement/Roo.Element} The new node
5410          */
5411         overwrite : function(el, o, returnElement)
5412         {
5413             el = Roo.getDom(el);
5414             if (o.ns) {
5415               
5416                 while (el.childNodes.length) {
5417                     el.removeChild(el.firstChild);
5418                 }
5419                 createDom(o, el);
5420             } else {
5421                 el.innerHTML = createHtml(o);   
5422             }
5423             
5424             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5425         },
5426     
5427         /**
5428          * Creates a new Roo.DomHelper.Template from the Dom object spec
5429          * @param {Object} o The Dom object spec (and children)
5430          * @return {Roo.DomHelper.Template} The new template
5431          */
5432         createTemplate : function(o){
5433             var html = createHtml(o);
5434             return new Roo.Template(html);
5435         },
5436          /**
5437          * Updates the first element with the spec from the o (replacing if necessary)
5438          * This iterates through the children, and updates attributes / children etc..
5439          * @param {String/HTMLElement/Element} el The context element
5440          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5441          */
5442         
5443         update : function(el, o)
5444         {
5445             updateNode(Roo.getDom(el), createDom(o));
5446             
5447         }
5448         
5449         
5450     };
5451 }();
5452 /*
5453  * Based on:
5454  * Ext JS Library 1.1.1
5455  * Copyright(c) 2006-2007, Ext JS, LLC.
5456  *
5457  * Originally Released Under LGPL - original licence link has changed is not relivant.
5458  *
5459  * Fork - LGPL
5460  * <script type="text/javascript">
5461  */
5462  
5463 /**
5464 * @class Roo.Template
5465 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5466 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5467 * Usage:
5468 <pre><code>
5469 var t = new Roo.Template({
5470     html :  '&lt;div name="{id}"&gt;' + 
5471         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5472         '&lt;/div&gt;',
5473     myformat: function (value, allValues) {
5474         return 'XX' + value;
5475     }
5476 });
5477 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5478 </code></pre>
5479 * For more information see this blog post with examples:
5480 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5481      - Create Elements using DOM, HTML fragments and Templates</a>. 
5482 * @constructor
5483 * @param {Object} cfg - Configuration object.
5484 */
5485 Roo.Template = function(cfg){
5486     // BC!
5487     if(cfg instanceof Array){
5488         cfg = cfg.join("");
5489     }else if(arguments.length > 1){
5490         cfg = Array.prototype.join.call(arguments, "");
5491     }
5492     
5493     
5494     if (typeof(cfg) == 'object') {
5495         Roo.apply(this,cfg)
5496     } else {
5497         // bc
5498         this.html = cfg;
5499     }
5500     if (this.url) {
5501         this.load();
5502     }
5503     
5504 };
5505 Roo.Template.prototype = {
5506     
5507     /**
5508      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5509      */
5510     onLoad : false,
5511     
5512     
5513     /**
5514      * @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..
5515      *                    it should be fixed so that template is observable...
5516      */
5517     url : false,
5518     /**
5519      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5520      */
5521     html : '',
5522     
5523     
5524     compiled : false,
5525     loaded : false,
5526     /**
5527      * Returns an HTML fragment of this template with the specified values applied.
5528      * @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'})
5529      * @return {String} The HTML fragment
5530      */
5531     
5532    
5533     
5534     applyTemplate : function(values){
5535         //Roo.log(["applyTemplate", values]);
5536         try {
5537            
5538             if(this.compiled){
5539                 return this.compiled(values);
5540             }
5541             var useF = this.disableFormats !== true;
5542             var fm = Roo.util.Format, tpl = this;
5543             var fn = function(m, name, format, args){
5544                 if(format && useF){
5545                     if(format.substr(0, 5) == "this."){
5546                         return tpl.call(format.substr(5), values[name], values);
5547                     }else{
5548                         if(args){
5549                             // quoted values are required for strings in compiled templates, 
5550                             // but for non compiled we need to strip them
5551                             // quoted reversed for jsmin
5552                             var re = /^\s*['"](.*)["']\s*$/;
5553                             args = args.split(',');
5554                             for(var i = 0, len = args.length; i < len; i++){
5555                                 args[i] = args[i].replace(re, "$1");
5556                             }
5557                             args = [values[name]].concat(args);
5558                         }else{
5559                             args = [values[name]];
5560                         }
5561                         return fm[format].apply(fm, args);
5562                     }
5563                 }else{
5564                     return values[name] !== undefined ? values[name] : "";
5565                 }
5566             };
5567             return this.html.replace(this.re, fn);
5568         } catch (e) {
5569             Roo.log(e);
5570             throw e;
5571         }
5572          
5573     },
5574     
5575     loading : false,
5576       
5577     load : function ()
5578     {
5579          
5580         if (this.loading) {
5581             return;
5582         }
5583         var _t = this;
5584         
5585         this.loading = true;
5586         this.compiled = false;
5587         
5588         var cx = new Roo.data.Connection();
5589         cx.request({
5590             url : this.url,
5591             method : 'GET',
5592             success : function (response) {
5593                 _t.loading = false;
5594                 _t.url = false;
5595                 
5596                 _t.set(response.responseText,true);
5597                 _t.loaded = true;
5598                 if (_t.onLoad) {
5599                     _t.onLoad();
5600                 }
5601              },
5602             failure : function(response) {
5603                 Roo.log("Template failed to load from " + _t.url);
5604                 _t.loading = false;
5605             }
5606         });
5607     },
5608
5609     /**
5610      * Sets the HTML used as the template and optionally compiles it.
5611      * @param {String} html
5612      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5613      * @return {Roo.Template} this
5614      */
5615     set : function(html, compile){
5616         this.html = html;
5617         this.compiled = false;
5618         if(compile){
5619             this.compile();
5620         }
5621         return this;
5622     },
5623     
5624     /**
5625      * True to disable format functions (defaults to false)
5626      * @type Boolean
5627      */
5628     disableFormats : false,
5629     
5630     /**
5631     * The regular expression used to match template variables 
5632     * @type RegExp
5633     * @property 
5634     */
5635     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5636     
5637     /**
5638      * Compiles the template into an internal function, eliminating the RegEx overhead.
5639      * @return {Roo.Template} this
5640      */
5641     compile : function(){
5642         var fm = Roo.util.Format;
5643         var useF = this.disableFormats !== true;
5644         var sep = Roo.isGecko ? "+" : ",";
5645         var fn = function(m, name, format, args){
5646             if(format && useF){
5647                 args = args ? ',' + args : "";
5648                 if(format.substr(0, 5) != "this."){
5649                     format = "fm." + format + '(';
5650                 }else{
5651                     format = 'this.call("'+ format.substr(5) + '", ';
5652                     args = ", values";
5653                 }
5654             }else{
5655                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5656             }
5657             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5658         };
5659         var body;
5660         // branched to use + in gecko and [].join() in others
5661         if(Roo.isGecko){
5662             body = "this.compiled = function(values){ return '" +
5663                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5664                     "';};";
5665         }else{
5666             body = ["this.compiled = function(values){ return ['"];
5667             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5668             body.push("'].join('');};");
5669             body = body.join('');
5670         }
5671         /**
5672          * eval:var:values
5673          * eval:var:fm
5674          */
5675         eval(body);
5676         return this;
5677     },
5678     
5679     // private function used to call members
5680     call : function(fnName, value, allValues){
5681         return this[fnName](value, allValues);
5682     },
5683     
5684     /**
5685      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5686      * @param {String/HTMLElement/Roo.Element} el The context element
5687      * @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'})
5688      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5689      * @return {HTMLElement/Roo.Element} The new node or Element
5690      */
5691     insertFirst: function(el, values, returnElement){
5692         return this.doInsert('afterBegin', el, values, returnElement);
5693     },
5694
5695     /**
5696      * Applies the supplied values to the template and inserts the new node(s) before el.
5697      * @param {String/HTMLElement/Roo.Element} el The context element
5698      * @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'})
5699      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5700      * @return {HTMLElement/Roo.Element} The new node or Element
5701      */
5702     insertBefore: function(el, values, returnElement){
5703         return this.doInsert('beforeBegin', el, values, returnElement);
5704     },
5705
5706     /**
5707      * Applies the supplied values to the template and inserts the new node(s) after el.
5708      * @param {String/HTMLElement/Roo.Element} el The context element
5709      * @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'})
5710      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5711      * @return {HTMLElement/Roo.Element} The new node or Element
5712      */
5713     insertAfter : function(el, values, returnElement){
5714         return this.doInsert('afterEnd', el, values, returnElement);
5715     },
5716     
5717     /**
5718      * Applies the supplied values to the template and appends the new node(s) to el.
5719      * @param {String/HTMLElement/Roo.Element} el The context element
5720      * @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'})
5721      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5722      * @return {HTMLElement/Roo.Element} The new node or Element
5723      */
5724     append : function(el, values, returnElement){
5725         return this.doInsert('beforeEnd', el, values, returnElement);
5726     },
5727
5728     doInsert : function(where, el, values, returnEl){
5729         el = Roo.getDom(el);
5730         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5731         return returnEl ? Roo.get(newNode, true) : newNode;
5732     },
5733
5734     /**
5735      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5736      * @param {String/HTMLElement/Roo.Element} el The context element
5737      * @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'})
5738      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5739      * @return {HTMLElement/Roo.Element} The new node or Element
5740      */
5741     overwrite : function(el, values, returnElement){
5742         el = Roo.getDom(el);
5743         el.innerHTML = this.applyTemplate(values);
5744         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5745     }
5746 };
5747 /**
5748  * Alias for {@link #applyTemplate}
5749  * @method
5750  */
5751 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5752
5753 // backwards compat
5754 Roo.DomHelper.Template = Roo.Template;
5755
5756 /**
5757  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5758  * @param {String/HTMLElement} el A DOM element or its id
5759  * @returns {Roo.Template} The created template
5760  * @static
5761  */
5762 Roo.Template.from = function(el){
5763     el = Roo.getDom(el);
5764     return new Roo.Template(el.value || el.innerHTML);
5765 };/*
5766  * Based on:
5767  * Ext JS Library 1.1.1
5768  * Copyright(c) 2006-2007, Ext JS, LLC.
5769  *
5770  * Originally Released Under LGPL - original licence link has changed is not relivant.
5771  *
5772  * Fork - LGPL
5773  * <script type="text/javascript">
5774  */
5775  
5776
5777 /*
5778  * This is code is also distributed under MIT license for use
5779  * with jQuery and prototype JavaScript libraries.
5780  */
5781 /**
5782  * @class Roo.DomQuery
5783 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).
5784 <p>
5785 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>
5786
5787 <p>
5788 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.
5789 </p>
5790 <h4>Element Selectors:</h4>
5791 <ul class="list">
5792     <li> <b>*</b> any element</li>
5793     <li> <b>E</b> an element with the tag E</li>
5794     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5795     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5796     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5797     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5798 </ul>
5799 <h4>Attribute Selectors:</h4>
5800 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5801 <ul class="list">
5802     <li> <b>E[foo]</b> has an attribute "foo"</li>
5803     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5804     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5805     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5806     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5807     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5808     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5809 </ul>
5810 <h4>Pseudo Classes:</h4>
5811 <ul class="list">
5812     <li> <b>E:first-child</b> E is the first child of its parent</li>
5813     <li> <b>E:last-child</b> E is the last child of its parent</li>
5814     <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>
5815     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5816     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5817     <li> <b>E:only-child</b> E is the only child of its parent</li>
5818     <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>
5819     <li> <b>E:first</b> the first E in the resultset</li>
5820     <li> <b>E:last</b> the last E in the resultset</li>
5821     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5822     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5823     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5824     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5825     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5826     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5827     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5828     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5829     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5830 </ul>
5831 <h4>CSS Value Selectors:</h4>
5832 <ul class="list">
5833     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5834     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5835     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5836     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5837     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5838     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5839 </ul>
5840  * @static
5841  */
5842 Roo.DomQuery = function(){
5843     var cache = {}, simpleCache = {}, valueCache = {};
5844     var nonSpace = /\S/;
5845     var trimRe = /^\s+|\s+$/g;
5846     var tplRe = /\{(\d+)\}/g;
5847     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5848     var tagTokenRe = /^(#)?([\w-\*]+)/;
5849     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5850
5851     function child(p, index){
5852         var i = 0;
5853         var n = p.firstChild;
5854         while(n){
5855             if(n.nodeType == 1){
5856                if(++i == index){
5857                    return n;
5858                }
5859             }
5860             n = n.nextSibling;
5861         }
5862         return null;
5863     };
5864
5865     function next(n){
5866         while((n = n.nextSibling) && n.nodeType != 1);
5867         return n;
5868     };
5869
5870     function prev(n){
5871         while((n = n.previousSibling) && n.nodeType != 1);
5872         return n;
5873     };
5874
5875     function children(d){
5876         var n = d.firstChild, ni = -1;
5877             while(n){
5878                 var nx = n.nextSibling;
5879                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5880                     d.removeChild(n);
5881                 }else{
5882                     n.nodeIndex = ++ni;
5883                 }
5884                 n = nx;
5885             }
5886             return this;
5887         };
5888
5889     function byClassName(c, a, v){
5890         if(!v){
5891             return c;
5892         }
5893         var r = [], ri = -1, cn;
5894         for(var i = 0, ci; ci = c[i]; i++){
5895             
5896             
5897             if((' '+
5898                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5899                  +' ').indexOf(v) != -1){
5900                 r[++ri] = ci;
5901             }
5902         }
5903         return r;
5904     };
5905
5906     function attrValue(n, attr){
5907         if(!n.tagName && typeof n.length != "undefined"){
5908             n = n[0];
5909         }
5910         if(!n){
5911             return null;
5912         }
5913         if(attr == "for"){
5914             return n.htmlFor;
5915         }
5916         if(attr == "class" || attr == "className"){
5917             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5918         }
5919         return n.getAttribute(attr) || n[attr];
5920
5921     };
5922
5923     function getNodes(ns, mode, tagName){
5924         var result = [], ri = -1, cs;
5925         if(!ns){
5926             return result;
5927         }
5928         tagName = tagName || "*";
5929         if(typeof ns.getElementsByTagName != "undefined"){
5930             ns = [ns];
5931         }
5932         if(!mode){
5933             for(var i = 0, ni; ni = ns[i]; i++){
5934                 cs = ni.getElementsByTagName(tagName);
5935                 for(var j = 0, ci; ci = cs[j]; j++){
5936                     result[++ri] = ci;
5937                 }
5938             }
5939         }else if(mode == "/" || mode == ">"){
5940             var utag = tagName.toUpperCase();
5941             for(var i = 0, ni, cn; ni = ns[i]; i++){
5942                 cn = ni.children || ni.childNodes;
5943                 for(var j = 0, cj; cj = cn[j]; j++){
5944                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5945                         result[++ri] = cj;
5946                     }
5947                 }
5948             }
5949         }else if(mode == "+"){
5950             var utag = tagName.toUpperCase();
5951             for(var i = 0, n; n = ns[i]; i++){
5952                 while((n = n.nextSibling) && n.nodeType != 1);
5953                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5954                     result[++ri] = n;
5955                 }
5956             }
5957         }else if(mode == "~"){
5958             for(var i = 0, n; n = ns[i]; i++){
5959                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5960                 if(n){
5961                     result[++ri] = n;
5962                 }
5963             }
5964         }
5965         return result;
5966     };
5967
5968     function concat(a, b){
5969         if(b.slice){
5970             return a.concat(b);
5971         }
5972         for(var i = 0, l = b.length; i < l; i++){
5973             a[a.length] = b[i];
5974         }
5975         return a;
5976     }
5977
5978     function byTag(cs, tagName){
5979         if(cs.tagName || cs == document){
5980             cs = [cs];
5981         }
5982         if(!tagName){
5983             return cs;
5984         }
5985         var r = [], ri = -1;
5986         tagName = tagName.toLowerCase();
5987         for(var i = 0, ci; ci = cs[i]; i++){
5988             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5989                 r[++ri] = ci;
5990             }
5991         }
5992         return r;
5993     };
5994
5995     function byId(cs, attr, id){
5996         if(cs.tagName || cs == document){
5997             cs = [cs];
5998         }
5999         if(!id){
6000             return cs;
6001         }
6002         var r = [], ri = -1;
6003         for(var i = 0,ci; ci = cs[i]; i++){
6004             if(ci && ci.id == id){
6005                 r[++ri] = ci;
6006                 return r;
6007             }
6008         }
6009         return r;
6010     };
6011
6012     function byAttribute(cs, attr, value, op, custom){
6013         var r = [], ri = -1, st = custom=="{";
6014         var f = Roo.DomQuery.operators[op];
6015         for(var i = 0, ci; ci = cs[i]; i++){
6016             var a;
6017             if(st){
6018                 a = Roo.DomQuery.getStyle(ci, attr);
6019             }
6020             else if(attr == "class" || attr == "className"){
6021                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6022             }else if(attr == "for"){
6023                 a = ci.htmlFor;
6024             }else if(attr == "href"){
6025                 a = ci.getAttribute("href", 2);
6026             }else{
6027                 a = ci.getAttribute(attr);
6028             }
6029             if((f && f(a, value)) || (!f && a)){
6030                 r[++ri] = ci;
6031             }
6032         }
6033         return r;
6034     };
6035
6036     function byPseudo(cs, name, value){
6037         return Roo.DomQuery.pseudos[name](cs, value);
6038     };
6039
6040     // This is for IE MSXML which does not support expandos.
6041     // IE runs the same speed using setAttribute, however FF slows way down
6042     // and Safari completely fails so they need to continue to use expandos.
6043     var isIE = window.ActiveXObject ? true : false;
6044
6045     // this eval is stop the compressor from
6046     // renaming the variable to something shorter
6047     
6048     /** eval:var:batch */
6049     var batch = 30803; 
6050
6051     var key = 30803;
6052
6053     function nodupIEXml(cs){
6054         var d = ++key;
6055         cs[0].setAttribute("_nodup", d);
6056         var r = [cs[0]];
6057         for(var i = 1, len = cs.length; i < len; i++){
6058             var c = cs[i];
6059             if(!c.getAttribute("_nodup") != d){
6060                 c.setAttribute("_nodup", d);
6061                 r[r.length] = c;
6062             }
6063         }
6064         for(var i = 0, len = cs.length; i < len; i++){
6065             cs[i].removeAttribute("_nodup");
6066         }
6067         return r;
6068     }
6069
6070     function nodup(cs){
6071         if(!cs){
6072             return [];
6073         }
6074         var len = cs.length, c, i, r = cs, cj, ri = -1;
6075         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6076             return cs;
6077         }
6078         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6079             return nodupIEXml(cs);
6080         }
6081         var d = ++key;
6082         cs[0]._nodup = d;
6083         for(i = 1; c = cs[i]; i++){
6084             if(c._nodup != d){
6085                 c._nodup = d;
6086             }else{
6087                 r = [];
6088                 for(var j = 0; j < i; j++){
6089                     r[++ri] = cs[j];
6090                 }
6091                 for(j = i+1; cj = cs[j]; j++){
6092                     if(cj._nodup != d){
6093                         cj._nodup = d;
6094                         r[++ri] = cj;
6095                     }
6096                 }
6097                 return r;
6098             }
6099         }
6100         return r;
6101     }
6102
6103     function quickDiffIEXml(c1, c2){
6104         var d = ++key;
6105         for(var i = 0, len = c1.length; i < len; i++){
6106             c1[i].setAttribute("_qdiff", d);
6107         }
6108         var r = [];
6109         for(var i = 0, len = c2.length; i < len; i++){
6110             if(c2[i].getAttribute("_qdiff") != d){
6111                 r[r.length] = c2[i];
6112             }
6113         }
6114         for(var i = 0, len = c1.length; i < len; i++){
6115            c1[i].removeAttribute("_qdiff");
6116         }
6117         return r;
6118     }
6119
6120     function quickDiff(c1, c2){
6121         var len1 = c1.length;
6122         if(!len1){
6123             return c2;
6124         }
6125         if(isIE && c1[0].selectSingleNode){
6126             return quickDiffIEXml(c1, c2);
6127         }
6128         var d = ++key;
6129         for(var i = 0; i < len1; i++){
6130             c1[i]._qdiff = d;
6131         }
6132         var r = [];
6133         for(var i = 0, len = c2.length; i < len; i++){
6134             if(c2[i]._qdiff != d){
6135                 r[r.length] = c2[i];
6136             }
6137         }
6138         return r;
6139     }
6140
6141     function quickId(ns, mode, root, id){
6142         if(ns == root){
6143            var d = root.ownerDocument || root;
6144            return d.getElementById(id);
6145         }
6146         ns = getNodes(ns, mode, "*");
6147         return byId(ns, null, id);
6148     }
6149
6150     return {
6151         getStyle : function(el, name){
6152             return Roo.fly(el).getStyle(name);
6153         },
6154         /**
6155          * Compiles a selector/xpath query into a reusable function. The returned function
6156          * takes one parameter "root" (optional), which is the context node from where the query should start.
6157          * @param {String} selector The selector/xpath query
6158          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6159          * @return {Function}
6160          */
6161         compile : function(path, type){
6162             type = type || "select";
6163             
6164             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6165             var q = path, mode, lq;
6166             var tk = Roo.DomQuery.matchers;
6167             var tklen = tk.length;
6168             var mm;
6169
6170             // accept leading mode switch
6171             var lmode = q.match(modeRe);
6172             if(lmode && lmode[1]){
6173                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6174                 q = q.replace(lmode[1], "");
6175             }
6176             // strip leading slashes
6177             while(path.substr(0, 1)=="/"){
6178                 path = path.substr(1);
6179             }
6180
6181             while(q && lq != q){
6182                 lq = q;
6183                 var tm = q.match(tagTokenRe);
6184                 if(type == "select"){
6185                     if(tm){
6186                         if(tm[1] == "#"){
6187                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6188                         }else{
6189                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6190                         }
6191                         q = q.replace(tm[0], "");
6192                     }else if(q.substr(0, 1) != '@'){
6193                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6194                     }
6195                 }else{
6196                     if(tm){
6197                         if(tm[1] == "#"){
6198                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6199                         }else{
6200                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6201                         }
6202                         q = q.replace(tm[0], "");
6203                     }
6204                 }
6205                 while(!(mm = q.match(modeRe))){
6206                     var matched = false;
6207                     for(var j = 0; j < tklen; j++){
6208                         var t = tk[j];
6209                         var m = q.match(t.re);
6210                         if(m){
6211                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6212                                                     return m[i];
6213                                                 });
6214                             q = q.replace(m[0], "");
6215                             matched = true;
6216                             break;
6217                         }
6218                     }
6219                     // prevent infinite loop on bad selector
6220                     if(!matched){
6221                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6222                     }
6223                 }
6224                 if(mm[1]){
6225                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6226                     q = q.replace(mm[1], "");
6227                 }
6228             }
6229             fn[fn.length] = "return nodup(n);\n}";
6230             
6231              /** 
6232               * list of variables that need from compression as they are used by eval.
6233              *  eval:var:batch 
6234              *  eval:var:nodup
6235              *  eval:var:byTag
6236              *  eval:var:ById
6237              *  eval:var:getNodes
6238              *  eval:var:quickId
6239              *  eval:var:mode
6240              *  eval:var:root
6241              *  eval:var:n
6242              *  eval:var:byClassName
6243              *  eval:var:byPseudo
6244              *  eval:var:byAttribute
6245              *  eval:var:attrValue
6246              * 
6247              **/ 
6248             eval(fn.join(""));
6249             return f;
6250         },
6251
6252         /**
6253          * Selects a group of elements.
6254          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6255          * @param {Node} root (optional) The start of the query (defaults to document).
6256          * @return {Array}
6257          */
6258         select : function(path, root, type){
6259             if(!root || root == document){
6260                 root = document;
6261             }
6262             if(typeof root == "string"){
6263                 root = document.getElementById(root);
6264             }
6265             var paths = path.split(",");
6266             var results = [];
6267             for(var i = 0, len = paths.length; i < len; i++){
6268                 var p = paths[i].replace(trimRe, "");
6269                 if(!cache[p]){
6270                     cache[p] = Roo.DomQuery.compile(p);
6271                     if(!cache[p]){
6272                         throw p + " is not a valid selector";
6273                     }
6274                 }
6275                 var result = cache[p](root);
6276                 if(result && result != document){
6277                     results = results.concat(result);
6278                 }
6279             }
6280             if(paths.length > 1){
6281                 return nodup(results);
6282             }
6283             return results;
6284         },
6285
6286         /**
6287          * Selects a single element.
6288          * @param {String} selector The selector/xpath query
6289          * @param {Node} root (optional) The start of the query (defaults to document).
6290          * @return {Element}
6291          */
6292         selectNode : function(path, root){
6293             return Roo.DomQuery.select(path, root)[0];
6294         },
6295
6296         /**
6297          * Selects the value of a node, optionally replacing null with the defaultValue.
6298          * @param {String} selector The selector/xpath query
6299          * @param {Node} root (optional) The start of the query (defaults to document).
6300          * @param {String} defaultValue
6301          */
6302         selectValue : function(path, root, defaultValue){
6303             path = path.replace(trimRe, "");
6304             if(!valueCache[path]){
6305                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6306             }
6307             var n = valueCache[path](root);
6308             n = n[0] ? n[0] : n;
6309             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6310             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6311         },
6312
6313         /**
6314          * Selects the value of a node, parsing integers and floats.
6315          * @param {String} selector The selector/xpath query
6316          * @param {Node} root (optional) The start of the query (defaults to document).
6317          * @param {Number} defaultValue
6318          * @return {Number}
6319          */
6320         selectNumber : function(path, root, defaultValue){
6321             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6322             return parseFloat(v);
6323         },
6324
6325         /**
6326          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6327          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6328          * @param {String} selector The simple selector to test
6329          * @return {Boolean}
6330          */
6331         is : function(el, ss){
6332             if(typeof el == "string"){
6333                 el = document.getElementById(el);
6334             }
6335             var isArray = (el instanceof Array);
6336             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6337             return isArray ? (result.length == el.length) : (result.length > 0);
6338         },
6339
6340         /**
6341          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6342          * @param {Array} el An array of elements to filter
6343          * @param {String} selector The simple selector to test
6344          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6345          * the selector instead of the ones that match
6346          * @return {Array}
6347          */
6348         filter : function(els, ss, nonMatches){
6349             ss = ss.replace(trimRe, "");
6350             if(!simpleCache[ss]){
6351                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6352             }
6353             var result = simpleCache[ss](els);
6354             return nonMatches ? quickDiff(result, els) : result;
6355         },
6356
6357         /**
6358          * Collection of matching regular expressions and code snippets.
6359          */
6360         matchers : [{
6361                 re: /^\.([\w-]+)/,
6362                 select: 'n = byClassName(n, null, " {1} ");'
6363             }, {
6364                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6365                 select: 'n = byPseudo(n, "{1}", "{2}");'
6366             },{
6367                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6368                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6369             }, {
6370                 re: /^#([\w-]+)/,
6371                 select: 'n = byId(n, null, "{1}");'
6372             },{
6373                 re: /^@([\w-]+)/,
6374                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6375             }
6376         ],
6377
6378         /**
6379          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6380          * 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;.
6381          */
6382         operators : {
6383             "=" : function(a, v){
6384                 return a == v;
6385             },
6386             "!=" : function(a, v){
6387                 return a != v;
6388             },
6389             "^=" : function(a, v){
6390                 return a && a.substr(0, v.length) == v;
6391             },
6392             "$=" : function(a, v){
6393                 return a && a.substr(a.length-v.length) == v;
6394             },
6395             "*=" : function(a, v){
6396                 return a && a.indexOf(v) !== -1;
6397             },
6398             "%=" : function(a, v){
6399                 return (a % v) == 0;
6400             },
6401             "|=" : function(a, v){
6402                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6403             },
6404             "~=" : function(a, v){
6405                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6406             }
6407         },
6408
6409         /**
6410          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6411          * and the argument (if any) supplied in the selector.
6412          */
6413         pseudos : {
6414             "first-child" : function(c){
6415                 var r = [], ri = -1, n;
6416                 for(var i = 0, ci; ci = n = c[i]; i++){
6417                     while((n = n.previousSibling) && n.nodeType != 1);
6418                     if(!n){
6419                         r[++ri] = ci;
6420                     }
6421                 }
6422                 return r;
6423             },
6424
6425             "last-child" : function(c){
6426                 var r = [], ri = -1, n;
6427                 for(var i = 0, ci; ci = n = c[i]; i++){
6428                     while((n = n.nextSibling) && n.nodeType != 1);
6429                     if(!n){
6430                         r[++ri] = ci;
6431                     }
6432                 }
6433                 return r;
6434             },
6435
6436             "nth-child" : function(c, a) {
6437                 var r = [], ri = -1;
6438                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6439                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6440                 for(var i = 0, n; n = c[i]; i++){
6441                     var pn = n.parentNode;
6442                     if (batch != pn._batch) {
6443                         var j = 0;
6444                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6445                             if(cn.nodeType == 1){
6446                                cn.nodeIndex = ++j;
6447                             }
6448                         }
6449                         pn._batch = batch;
6450                     }
6451                     if (f == 1) {
6452                         if (l == 0 || n.nodeIndex == l){
6453                             r[++ri] = n;
6454                         }
6455                     } else if ((n.nodeIndex + l) % f == 0){
6456                         r[++ri] = n;
6457                     }
6458                 }
6459
6460                 return r;
6461             },
6462
6463             "only-child" : function(c){
6464                 var r = [], ri = -1;;
6465                 for(var i = 0, ci; ci = c[i]; i++){
6466                     if(!prev(ci) && !next(ci)){
6467                         r[++ri] = ci;
6468                     }
6469                 }
6470                 return r;
6471             },
6472
6473             "empty" : function(c){
6474                 var r = [], ri = -1;
6475                 for(var i = 0, ci; ci = c[i]; i++){
6476                     var cns = ci.childNodes, j = 0, cn, empty = true;
6477                     while(cn = cns[j]){
6478                         ++j;
6479                         if(cn.nodeType == 1 || cn.nodeType == 3){
6480                             empty = false;
6481                             break;
6482                         }
6483                     }
6484                     if(empty){
6485                         r[++ri] = ci;
6486                     }
6487                 }
6488                 return r;
6489             },
6490
6491             "contains" : function(c, v){
6492                 var r = [], ri = -1;
6493                 for(var i = 0, ci; ci = c[i]; i++){
6494                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6495                         r[++ri] = ci;
6496                     }
6497                 }
6498                 return r;
6499             },
6500
6501             "nodeValue" : function(c, v){
6502                 var r = [], ri = -1;
6503                 for(var i = 0, ci; ci = c[i]; i++){
6504                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6505                         r[++ri] = ci;
6506                     }
6507                 }
6508                 return r;
6509             },
6510
6511             "checked" : function(c){
6512                 var r = [], ri = -1;
6513                 for(var i = 0, ci; ci = c[i]; i++){
6514                     if(ci.checked == true){
6515                         r[++ri] = ci;
6516                     }
6517                 }
6518                 return r;
6519             },
6520
6521             "not" : function(c, ss){
6522                 return Roo.DomQuery.filter(c, ss, true);
6523             },
6524
6525             "odd" : function(c){
6526                 return this["nth-child"](c, "odd");
6527             },
6528
6529             "even" : function(c){
6530                 return this["nth-child"](c, "even");
6531             },
6532
6533             "nth" : function(c, a){
6534                 return c[a-1] || [];
6535             },
6536
6537             "first" : function(c){
6538                 return c[0] || [];
6539             },
6540
6541             "last" : function(c){
6542                 return c[c.length-1] || [];
6543             },
6544
6545             "has" : function(c, ss){
6546                 var s = Roo.DomQuery.select;
6547                 var r = [], ri = -1;
6548                 for(var i = 0, ci; ci = c[i]; i++){
6549                     if(s(ss, ci).length > 0){
6550                         r[++ri] = ci;
6551                     }
6552                 }
6553                 return r;
6554             },
6555
6556             "next" : function(c, ss){
6557                 var is = Roo.DomQuery.is;
6558                 var r = [], ri = -1;
6559                 for(var i = 0, ci; ci = c[i]; i++){
6560                     var n = next(ci);
6561                     if(n && is(n, ss)){
6562                         r[++ri] = ci;
6563                     }
6564                 }
6565                 return r;
6566             },
6567
6568             "prev" : function(c, ss){
6569                 var is = Roo.DomQuery.is;
6570                 var r = [], ri = -1;
6571                 for(var i = 0, ci; ci = c[i]; i++){
6572                     var n = prev(ci);
6573                     if(n && is(n, ss)){
6574                         r[++ri] = ci;
6575                     }
6576                 }
6577                 return r;
6578             }
6579         }
6580     };
6581 }();
6582
6583 /**
6584  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6585  * @param {String} path The selector/xpath query
6586  * @param {Node} root (optional) The start of the query (defaults to document).
6587  * @return {Array}
6588  * @member Roo
6589  * @method query
6590  */
6591 Roo.query = Roo.DomQuery.select;
6592 /*
6593  * Based on:
6594  * Ext JS Library 1.1.1
6595  * Copyright(c) 2006-2007, Ext JS, LLC.
6596  *
6597  * Originally Released Under LGPL - original licence link has changed is not relivant.
6598  *
6599  * Fork - LGPL
6600  * <script type="text/javascript">
6601  */
6602
6603 /**
6604  * @class Roo.util.Observable
6605  * Base class that provides a common interface for publishing events. Subclasses are expected to
6606  * to have a property "events" with all the events defined.<br>
6607  * For example:
6608  * <pre><code>
6609  Employee = function(name){
6610     this.name = name;
6611     this.addEvents({
6612         "fired" : true,
6613         "quit" : true
6614     });
6615  }
6616  Roo.extend(Employee, Roo.util.Observable);
6617 </code></pre>
6618  * @param {Object} config properties to use (incuding events / listeners)
6619  */
6620
6621 Roo.util.Observable = function(cfg){
6622     
6623     cfg = cfg|| {};
6624     this.addEvents(cfg.events || {});
6625     if (cfg.events) {
6626         delete cfg.events; // make sure
6627     }
6628      
6629     Roo.apply(this, cfg);
6630     
6631     if(this.listeners){
6632         this.on(this.listeners);
6633         delete this.listeners;
6634     }
6635 };
6636 Roo.util.Observable.prototype = {
6637     /** 
6638  * @cfg {Object} listeners  list of events and functions to call for this object, 
6639  * For example :
6640  * <pre><code>
6641     listeners :  { 
6642        'click' : function(e) {
6643            ..... 
6644         } ,
6645         .... 
6646     } 
6647   </code></pre>
6648  */
6649     
6650     
6651     /**
6652      * Fires the specified event with the passed parameters (minus the event name).
6653      * @param {String} eventName
6654      * @param {Object...} args Variable number of parameters are passed to handlers
6655      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6656      */
6657     fireEvent : function(){
6658         var ce = this.events[arguments[0].toLowerCase()];
6659         if(typeof ce == "object"){
6660             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6661         }else{
6662             return true;
6663         }
6664     },
6665
6666     // private
6667     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6668
6669     /**
6670      * Appends an event handler to this component
6671      * @param {String}   eventName The type of event to listen for
6672      * @param {Function} handler The method the event invokes
6673      * @param {Object}   scope (optional) The scope in which to execute the handler
6674      * function. The handler function's "this" context.
6675      * @param {Object}   options (optional) An object containing handler configuration
6676      * properties. This may contain any of the following properties:<ul>
6677      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6678      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6679      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6680      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6681      * by the specified number of milliseconds. If the event fires again within that time, the original
6682      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6683      * </ul><br>
6684      * <p>
6685      * <b>Combining Options</b><br>
6686      * Using the options argument, it is possible to combine different types of listeners:<br>
6687      * <br>
6688      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6689                 <pre><code>
6690                 el.on('click', this.onClick, this, {
6691                         single: true,
6692                 delay: 100,
6693                 forumId: 4
6694                 });
6695                 </code></pre>
6696      * <p>
6697      * <b>Attaching multiple handlers in 1 call</b><br>
6698      * The method also allows for a single argument to be passed which is a config object containing properties
6699      * which specify multiple handlers.
6700      * <pre><code>
6701                 el.on({
6702                         'click': {
6703                         fn: this.onClick,
6704                         scope: this,
6705                         delay: 100
6706                 }, 
6707                 'mouseover': {
6708                         fn: this.onMouseOver,
6709                         scope: this
6710                 },
6711                 'mouseout': {
6712                         fn: this.onMouseOut,
6713                         scope: this
6714                 }
6715                 });
6716                 </code></pre>
6717      * <p>
6718      * Or a shorthand syntax which passes the same scope object to all handlers:
6719         <pre><code>
6720                 el.on({
6721                         'click': this.onClick,
6722                 'mouseover': this.onMouseOver,
6723                 'mouseout': this.onMouseOut,
6724                 scope: this
6725                 });
6726                 </code></pre>
6727      */
6728     addListener : function(eventName, fn, scope, o){
6729         if(typeof eventName == "object"){
6730             o = eventName;
6731             for(var e in o){
6732                 if(this.filterOptRe.test(e)){
6733                     continue;
6734                 }
6735                 if(typeof o[e] == "function"){
6736                     // shared options
6737                     this.addListener(e, o[e], o.scope,  o);
6738                 }else{
6739                     // individual options
6740                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6741                 }
6742             }
6743             return;
6744         }
6745         o = (!o || typeof o == "boolean") ? {} : o;
6746         eventName = eventName.toLowerCase();
6747         var ce = this.events[eventName] || true;
6748         if(typeof ce == "boolean"){
6749             ce = new Roo.util.Event(this, eventName);
6750             this.events[eventName] = ce;
6751         }
6752         ce.addListener(fn, scope, o);
6753     },
6754
6755     /**
6756      * Removes a listener
6757      * @param {String}   eventName     The type of event to listen for
6758      * @param {Function} handler        The handler to remove
6759      * @param {Object}   scope  (optional) The scope (this object) for the handler
6760      */
6761     removeListener : function(eventName, fn, scope){
6762         var ce = this.events[eventName.toLowerCase()];
6763         if(typeof ce == "object"){
6764             ce.removeListener(fn, scope);
6765         }
6766     },
6767
6768     /**
6769      * Removes all listeners for this object
6770      */
6771     purgeListeners : function(){
6772         for(var evt in this.events){
6773             if(typeof this.events[evt] == "object"){
6774                  this.events[evt].clearListeners();
6775             }
6776         }
6777     },
6778
6779     relayEvents : function(o, events){
6780         var createHandler = function(ename){
6781             return function(){
6782                  
6783                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6784             };
6785         };
6786         for(var i = 0, len = events.length; i < len; i++){
6787             var ename = events[i];
6788             if(!this.events[ename]){
6789                 this.events[ename] = true;
6790             };
6791             o.on(ename, createHandler(ename), this);
6792         }
6793     },
6794
6795     /**
6796      * Used to define events on this Observable
6797      * @param {Object} object The object with the events defined
6798      */
6799     addEvents : function(o){
6800         if(!this.events){
6801             this.events = {};
6802         }
6803         Roo.applyIf(this.events, o);
6804     },
6805
6806     /**
6807      * Checks to see if this object has any listeners for a specified event
6808      * @param {String} eventName The name of the event to check for
6809      * @return {Boolean} True if the event is being listened for, else false
6810      */
6811     hasListener : function(eventName){
6812         var e = this.events[eventName];
6813         return typeof e == "object" && e.listeners.length > 0;
6814     }
6815 };
6816 /**
6817  * Appends an event handler to this element (shorthand for addListener)
6818  * @param {String}   eventName     The type of event to listen for
6819  * @param {Function} handler        The method the event invokes
6820  * @param {Object}   scope (optional) The scope in which to execute the handler
6821  * function. The handler function's "this" context.
6822  * @param {Object}   options  (optional)
6823  * @method
6824  */
6825 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6826 /**
6827  * Removes a listener (shorthand for removeListener)
6828  * @param {String}   eventName     The type of event to listen for
6829  * @param {Function} handler        The handler to remove
6830  * @param {Object}   scope  (optional) The scope (this object) for the handler
6831  * @method
6832  */
6833 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6834
6835 /**
6836  * Starts capture on the specified Observable. All events will be passed
6837  * to the supplied function with the event name + standard signature of the event
6838  * <b>before</b> the event is fired. If the supplied function returns false,
6839  * the event will not fire.
6840  * @param {Observable} o The Observable to capture
6841  * @param {Function} fn The function to call
6842  * @param {Object} scope (optional) The scope (this object) for the fn
6843  * @static
6844  */
6845 Roo.util.Observable.capture = function(o, fn, scope){
6846     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6847 };
6848
6849 /**
6850  * Removes <b>all</b> added captures from the Observable.
6851  * @param {Observable} o The Observable to release
6852  * @static
6853  */
6854 Roo.util.Observable.releaseCapture = function(o){
6855     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6856 };
6857
6858 (function(){
6859
6860     var createBuffered = function(h, o, scope){
6861         var task = new Roo.util.DelayedTask();
6862         return function(){
6863             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6864         };
6865     };
6866
6867     var createSingle = function(h, e, fn, scope){
6868         return function(){
6869             e.removeListener(fn, scope);
6870             return h.apply(scope, arguments);
6871         };
6872     };
6873
6874     var createDelayed = function(h, o, scope){
6875         return function(){
6876             var args = Array.prototype.slice.call(arguments, 0);
6877             setTimeout(function(){
6878                 h.apply(scope, args);
6879             }, o.delay || 10);
6880         };
6881     };
6882
6883     Roo.util.Event = function(obj, name){
6884         this.name = name;
6885         this.obj = obj;
6886         this.listeners = [];
6887     };
6888
6889     Roo.util.Event.prototype = {
6890         addListener : function(fn, scope, options){
6891             var o = options || {};
6892             scope = scope || this.obj;
6893             if(!this.isListening(fn, scope)){
6894                 var l = {fn: fn, scope: scope, options: o};
6895                 var h = fn;
6896                 if(o.delay){
6897                     h = createDelayed(h, o, scope);
6898                 }
6899                 if(o.single){
6900                     h = createSingle(h, this, fn, scope);
6901                 }
6902                 if(o.buffer){
6903                     h = createBuffered(h, o, scope);
6904                 }
6905                 l.fireFn = h;
6906                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6907                     this.listeners.push(l);
6908                 }else{
6909                     this.listeners = this.listeners.slice(0);
6910                     this.listeners.push(l);
6911                 }
6912             }
6913         },
6914
6915         findListener : function(fn, scope){
6916             scope = scope || this.obj;
6917             var ls = this.listeners;
6918             for(var i = 0, len = ls.length; i < len; i++){
6919                 var l = ls[i];
6920                 if(l.fn == fn && l.scope == scope){
6921                     return i;
6922                 }
6923             }
6924             return -1;
6925         },
6926
6927         isListening : function(fn, scope){
6928             return this.findListener(fn, scope) != -1;
6929         },
6930
6931         removeListener : function(fn, scope){
6932             var index;
6933             if((index = this.findListener(fn, scope)) != -1){
6934                 if(!this.firing){
6935                     this.listeners.splice(index, 1);
6936                 }else{
6937                     this.listeners = this.listeners.slice(0);
6938                     this.listeners.splice(index, 1);
6939                 }
6940                 return true;
6941             }
6942             return false;
6943         },
6944
6945         clearListeners : function(){
6946             this.listeners = [];
6947         },
6948
6949         fire : function(){
6950             var ls = this.listeners, scope, len = ls.length;
6951             if(len > 0){
6952                 this.firing = true;
6953                 var args = Array.prototype.slice.call(arguments, 0);                
6954                 for(var i = 0; i < len; i++){
6955                     var l = ls[i];
6956                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6957                         this.firing = false;
6958                         return false;
6959                     }
6960                 }
6961                 this.firing = false;
6962             }
6963             return true;
6964         }
6965     };
6966 })();/*
6967  * RooJS Library 
6968  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6969  *
6970  * Licence LGPL 
6971  *
6972  */
6973  
6974 /**
6975  * @class Roo.Document
6976  * @extends Roo.util.Observable
6977  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6978  * 
6979  * @param {Object} config the methods and properties of the 'base' class for the application.
6980  * 
6981  *  Generic Page handler - implement this to start your app..
6982  * 
6983  * eg.
6984  *  MyProject = new Roo.Document({
6985         events : {
6986             'load' : true // your events..
6987         },
6988         listeners : {
6989             'ready' : function() {
6990                 // fired on Roo.onReady()
6991             }
6992         }
6993  * 
6994  */
6995 Roo.Document = function(cfg) {
6996      
6997     this.addEvents({ 
6998         'ready' : true
6999     });
7000     Roo.util.Observable.call(this,cfg);
7001     
7002     var _this = this;
7003     
7004     Roo.onReady(function() {
7005         _this.fireEvent('ready');
7006     },null,false);
7007     
7008     
7009 }
7010
7011 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7012  * Based on:
7013  * Ext JS Library 1.1.1
7014  * Copyright(c) 2006-2007, Ext JS, LLC.
7015  *
7016  * Originally Released Under LGPL - original licence link has changed is not relivant.
7017  *
7018  * Fork - LGPL
7019  * <script type="text/javascript">
7020  */
7021
7022 /**
7023  * @class Roo.EventManager
7024  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7025  * several useful events directly.
7026  * See {@link Roo.EventObject} for more details on normalized event objects.
7027  * @static
7028  */
7029 Roo.EventManager = function(){
7030     var docReadyEvent, docReadyProcId, docReadyState = false;
7031     var resizeEvent, resizeTask, textEvent, textSize;
7032     var E = Roo.lib.Event;
7033     var D = Roo.lib.Dom;
7034
7035     
7036     
7037
7038     var fireDocReady = function(){
7039         if(!docReadyState){
7040             docReadyState = true;
7041             Roo.isReady = true;
7042             if(docReadyProcId){
7043                 clearInterval(docReadyProcId);
7044             }
7045             if(Roo.isGecko || Roo.isOpera) {
7046                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7047             }
7048             if(Roo.isIE){
7049                 var defer = document.getElementById("ie-deferred-loader");
7050                 if(defer){
7051                     defer.onreadystatechange = null;
7052                     defer.parentNode.removeChild(defer);
7053                 }
7054             }
7055             if(docReadyEvent){
7056                 docReadyEvent.fire();
7057                 docReadyEvent.clearListeners();
7058             }
7059         }
7060     };
7061     
7062     var initDocReady = function(){
7063         docReadyEvent = new Roo.util.Event();
7064         if(Roo.isGecko || Roo.isOpera) {
7065             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7066         }else if(Roo.isIE){
7067             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7068             var defer = document.getElementById("ie-deferred-loader");
7069             defer.onreadystatechange = function(){
7070                 if(this.readyState == "complete"){
7071                     fireDocReady();
7072                 }
7073             };
7074         }else if(Roo.isSafari){ 
7075             docReadyProcId = setInterval(function(){
7076                 var rs = document.readyState;
7077                 if(rs == "complete") {
7078                     fireDocReady();     
7079                  }
7080             }, 10);
7081         }
7082         // no matter what, make sure it fires on load
7083         E.on(window, "load", fireDocReady);
7084     };
7085
7086     var createBuffered = function(h, o){
7087         var task = new Roo.util.DelayedTask(h);
7088         return function(e){
7089             // create new event object impl so new events don't wipe out properties
7090             e = new Roo.EventObjectImpl(e);
7091             task.delay(o.buffer, h, null, [e]);
7092         };
7093     };
7094
7095     var createSingle = function(h, el, ename, fn){
7096         return function(e){
7097             Roo.EventManager.removeListener(el, ename, fn);
7098             h(e);
7099         };
7100     };
7101
7102     var createDelayed = function(h, o){
7103         return function(e){
7104             // create new event object impl so new events don't wipe out properties
7105             e = new Roo.EventObjectImpl(e);
7106             setTimeout(function(){
7107                 h(e);
7108             }, o.delay || 10);
7109         };
7110     };
7111     var transitionEndVal = false;
7112     
7113     var transitionEnd = function()
7114     {
7115         if (transitionEndVal) {
7116             return transitionEndVal;
7117         }
7118         var el = document.createElement('div');
7119
7120         var transEndEventNames = {
7121             WebkitTransition : 'webkitTransitionEnd',
7122             MozTransition    : 'transitionend',
7123             OTransition      : 'oTransitionEnd otransitionend',
7124             transition       : 'transitionend'
7125         };
7126     
7127         for (var name in transEndEventNames) {
7128             if (el.style[name] !== undefined) {
7129                 transitionEndVal = transEndEventNames[name];
7130                 return  transitionEndVal ;
7131             }
7132         }
7133     }
7134     
7135   
7136
7137     var listen = function(element, ename, opt, fn, scope)
7138     {
7139         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7140         fn = fn || o.fn; scope = scope || o.scope;
7141         var el = Roo.getDom(element);
7142         
7143         
7144         if(!el){
7145             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7146         }
7147         
7148         if (ename == 'transitionend') {
7149             ename = transitionEnd();
7150         }
7151         var h = function(e){
7152             e = Roo.EventObject.setEvent(e);
7153             var t;
7154             if(o.delegate){
7155                 t = e.getTarget(o.delegate, el);
7156                 if(!t){
7157                     return;
7158                 }
7159             }else{
7160                 t = e.target;
7161             }
7162             if(o.stopEvent === true){
7163                 e.stopEvent();
7164             }
7165             if(o.preventDefault === true){
7166                e.preventDefault();
7167             }
7168             if(o.stopPropagation === true){
7169                 e.stopPropagation();
7170             }
7171
7172             if(o.normalized === false){
7173                 e = e.browserEvent;
7174             }
7175
7176             fn.call(scope || el, e, t, o);
7177         };
7178         if(o.delay){
7179             h = createDelayed(h, o);
7180         }
7181         if(o.single){
7182             h = createSingle(h, el, ename, fn);
7183         }
7184         if(o.buffer){
7185             h = createBuffered(h, o);
7186         }
7187         
7188         fn._handlers = fn._handlers || [];
7189         
7190         
7191         fn._handlers.push([Roo.id(el), ename, h]);
7192         
7193         
7194          
7195         E.on(el, ename, h); // this adds the actuall listener to the object..
7196         
7197         
7198         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7199             el.addEventListener("DOMMouseScroll", h, false);
7200             E.on(window, 'unload', function(){
7201                 el.removeEventListener("DOMMouseScroll", h, false);
7202             });
7203         }
7204         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7205             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7206         }
7207         return h;
7208     };
7209
7210     var stopListening = function(el, ename, fn){
7211         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7212         if(hds){
7213             for(var i = 0, len = hds.length; i < len; i++){
7214                 var h = hds[i];
7215                 if(h[0] == id && h[1] == ename){
7216                     hd = h[2];
7217                     hds.splice(i, 1);
7218                     break;
7219                 }
7220             }
7221         }
7222         E.un(el, ename, hd);
7223         el = Roo.getDom(el);
7224         if(ename == "mousewheel" && el.addEventListener){
7225             el.removeEventListener("DOMMouseScroll", hd, false);
7226         }
7227         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7228             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7229         }
7230     };
7231
7232     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7233     
7234     var pub = {
7235         
7236         
7237         /** 
7238          * Fix for doc tools
7239          * @scope Roo.EventManager
7240          */
7241         
7242         
7243         /** 
7244          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7245          * object with a Roo.EventObject
7246          * @param {Function} fn        The method the event invokes
7247          * @param {Object}   scope    An object that becomes the scope of the handler
7248          * @param {boolean}  override If true, the obj passed in becomes
7249          *                             the execution scope of the listener
7250          * @return {Function} The wrapped function
7251          * @deprecated
7252          */
7253         wrap : function(fn, scope, override){
7254             return function(e){
7255                 Roo.EventObject.setEvent(e);
7256                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7257             };
7258         },
7259         
7260         /**
7261      * Appends an event handler to an element (shorthand for addListener)
7262      * @param {String/HTMLElement}   element        The html element or id to assign the
7263      * @param {String}   eventName The type of event to listen for
7264      * @param {Function} handler The method the event invokes
7265      * @param {Object}   scope (optional) The scope in which to execute the handler
7266      * function. The handler function's "this" context.
7267      * @param {Object}   options (optional) An object containing handler configuration
7268      * properties. This may contain any of the following properties:<ul>
7269      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7270      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7271      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7272      * <li>preventDefault {Boolean} True to prevent the default action</li>
7273      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7274      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7275      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7276      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7277      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7278      * by the specified number of milliseconds. If the event fires again within that time, the original
7279      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7280      * </ul><br>
7281      * <p>
7282      * <b>Combining Options</b><br>
7283      * Using the options argument, it is possible to combine different types of listeners:<br>
7284      * <br>
7285      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7286      * Code:<pre><code>
7287 el.on('click', this.onClick, this, {
7288     single: true,
7289     delay: 100,
7290     stopEvent : true,
7291     forumId: 4
7292 });</code></pre>
7293      * <p>
7294      * <b>Attaching multiple handlers in 1 call</b><br>
7295       * The method also allows for a single argument to be passed which is a config object containing properties
7296      * which specify multiple handlers.
7297      * <p>
7298      * Code:<pre><code>
7299 el.on({
7300     'click' : {
7301         fn: this.onClick
7302         scope: this,
7303         delay: 100
7304     },
7305     'mouseover' : {
7306         fn: this.onMouseOver
7307         scope: this
7308     },
7309     'mouseout' : {
7310         fn: this.onMouseOut
7311         scope: this
7312     }
7313 });</code></pre>
7314      * <p>
7315      * Or a shorthand syntax:<br>
7316      * Code:<pre><code>
7317 el.on({
7318     'click' : this.onClick,
7319     'mouseover' : this.onMouseOver,
7320     'mouseout' : this.onMouseOut
7321     scope: this
7322 });</code></pre>
7323      */
7324         addListener : function(element, eventName, fn, scope, options){
7325             if(typeof eventName == "object"){
7326                 var o = eventName;
7327                 for(var e in o){
7328                     if(propRe.test(e)){
7329                         continue;
7330                     }
7331                     if(typeof o[e] == "function"){
7332                         // shared options
7333                         listen(element, e, o, o[e], o.scope);
7334                     }else{
7335                         // individual options
7336                         listen(element, e, o[e]);
7337                     }
7338                 }
7339                 return;
7340             }
7341             return listen(element, eventName, options, fn, scope);
7342         },
7343         
7344         /**
7345          * Removes an event handler
7346          *
7347          * @param {String/HTMLElement}   element        The id or html element to remove the 
7348          *                             event from
7349          * @param {String}   eventName     The type of event
7350          * @param {Function} fn
7351          * @return {Boolean} True if a listener was actually removed
7352          */
7353         removeListener : function(element, eventName, fn){
7354             return stopListening(element, eventName, fn);
7355         },
7356         
7357         /**
7358          * Fires when the document is ready (before onload and before images are loaded). Can be 
7359          * accessed shorthanded Roo.onReady().
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An  object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onDocumentReady : function(fn, scope, options){
7365             if(docReadyState){ // if it already fired
7366                 docReadyEvent.addListener(fn, scope, options);
7367                 docReadyEvent.fire();
7368                 docReadyEvent.clearListeners();
7369                 return;
7370             }
7371             if(!docReadyEvent){
7372                 initDocReady();
7373             }
7374             docReadyEvent.addListener(fn, scope, options);
7375         },
7376         
7377         /**
7378          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7379          * @param {Function} fn        The method the event invokes
7380          * @param {Object}   scope    An object that becomes the scope of the handler
7381          * @param {boolean}  options
7382          */
7383         onWindowResize : function(fn, scope, options)
7384         {
7385             if(!resizeEvent){
7386                 resizeEvent = new Roo.util.Event();
7387                 resizeTask = new Roo.util.DelayedTask(function(){
7388                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7389                 });
7390                 E.on(window, "resize", function()
7391                 {
7392                     if (Roo.isIE) {
7393                         resizeTask.delay(50);
7394                     } else {
7395                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7396                     }
7397                 });
7398             }
7399             resizeEvent.addListener(fn, scope, options);
7400         },
7401
7402         /**
7403          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7404          * @param {Function} fn        The method the event invokes
7405          * @param {Object}   scope    An object that becomes the scope of the handler
7406          * @param {boolean}  options
7407          */
7408         onTextResize : function(fn, scope, options){
7409             if(!textEvent){
7410                 textEvent = new Roo.util.Event();
7411                 var textEl = new Roo.Element(document.createElement('div'));
7412                 textEl.dom.className = 'x-text-resize';
7413                 textEl.dom.innerHTML = 'X';
7414                 textEl.appendTo(document.body);
7415                 textSize = textEl.dom.offsetHeight;
7416                 setInterval(function(){
7417                     if(textEl.dom.offsetHeight != textSize){
7418                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7419                     }
7420                 }, this.textResizeInterval);
7421             }
7422             textEvent.addListener(fn, scope, options);
7423         },
7424
7425         /**
7426          * Removes the passed window resize listener.
7427          * @param {Function} fn        The method the event invokes
7428          * @param {Object}   scope    The scope of handler
7429          */
7430         removeResizeListener : function(fn, scope){
7431             if(resizeEvent){
7432                 resizeEvent.removeListener(fn, scope);
7433             }
7434         },
7435
7436         // private
7437         fireResize : function(){
7438             if(resizeEvent){
7439                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7440             }   
7441         },
7442         /**
7443          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7444          */
7445         ieDeferSrc : false,
7446         /**
7447          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7448          */
7449         textResizeInterval : 50
7450     };
7451     
7452     /**
7453      * Fix for doc tools
7454      * @scopeAlias pub=Roo.EventManager
7455      */
7456     
7457      /**
7458      * Appends an event handler to an element (shorthand for addListener)
7459      * @param {String/HTMLElement}   element        The html element or id to assign the
7460      * @param {String}   eventName The type of event to listen for
7461      * @param {Function} handler The method the event invokes
7462      * @param {Object}   scope (optional) The scope in which to execute the handler
7463      * function. The handler function's "this" context.
7464      * @param {Object}   options (optional) An object containing handler configuration
7465      * properties. This may contain any of the following properties:<ul>
7466      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7467      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7468      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7469      * <li>preventDefault {Boolean} True to prevent the default action</li>
7470      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7471      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7472      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7473      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7474      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7475      * by the specified number of milliseconds. If the event fires again within that time, the original
7476      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7477      * </ul><br>
7478      * <p>
7479      * <b>Combining Options</b><br>
7480      * Using the options argument, it is possible to combine different types of listeners:<br>
7481      * <br>
7482      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7483      * Code:<pre><code>
7484 el.on('click', this.onClick, this, {
7485     single: true,
7486     delay: 100,
7487     stopEvent : true,
7488     forumId: 4
7489 });</code></pre>
7490      * <p>
7491      * <b>Attaching multiple handlers in 1 call</b><br>
7492       * The method also allows for a single argument to be passed which is a config object containing properties
7493      * which specify multiple handlers.
7494      * <p>
7495      * Code:<pre><code>
7496 el.on({
7497     'click' : {
7498         fn: this.onClick
7499         scope: this,
7500         delay: 100
7501     },
7502     'mouseover' : {
7503         fn: this.onMouseOver
7504         scope: this
7505     },
7506     'mouseout' : {
7507         fn: this.onMouseOut
7508         scope: this
7509     }
7510 });</code></pre>
7511      * <p>
7512      * Or a shorthand syntax:<br>
7513      * Code:<pre><code>
7514 el.on({
7515     'click' : this.onClick,
7516     'mouseover' : this.onMouseOver,
7517     'mouseout' : this.onMouseOut
7518     scope: this
7519 });</code></pre>
7520      */
7521     pub.on = pub.addListener;
7522     pub.un = pub.removeListener;
7523
7524     pub.stoppedMouseDownEvent = new Roo.util.Event();
7525     return pub;
7526 }();
7527 /**
7528   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7529   * @param {Function} fn        The method the event invokes
7530   * @param {Object}   scope    An  object that becomes the scope of the handler
7531   * @param {boolean}  override If true, the obj passed in becomes
7532   *                             the execution scope of the listener
7533   * @member Roo
7534   * @method onReady
7535  */
7536 Roo.onReady = Roo.EventManager.onDocumentReady;
7537
7538 Roo.onReady(function(){
7539     var bd = Roo.get(document.body);
7540     if(!bd){ return; }
7541
7542     var cls = [
7543             Roo.isIE ? "roo-ie"
7544             : Roo.isIE11 ? "roo-ie11"
7545             : Roo.isEdge ? "roo-edge"
7546             : Roo.isGecko ? "roo-gecko"
7547             : Roo.isOpera ? "roo-opera"
7548             : Roo.isSafari ? "roo-safari" : ""];
7549
7550     if(Roo.isMac){
7551         cls.push("roo-mac");
7552     }
7553     if(Roo.isLinux){
7554         cls.push("roo-linux");
7555     }
7556     if(Roo.isIOS){
7557         cls.push("roo-ios");
7558     }
7559     if(Roo.isTouch){
7560         cls.push("roo-touch");
7561     }
7562     if(Roo.isBorderBox){
7563         cls.push('roo-border-box');
7564     }
7565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7566         var p = bd.dom.parentNode;
7567         if(p){
7568             p.className += ' roo-strict';
7569         }
7570     }
7571     bd.addClass(cls.join(' '));
7572 });
7573
7574 /**
7575  * @class Roo.EventObject
7576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7578  * Example:
7579  * <pre><code>
7580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7581     e.preventDefault();
7582     var target = e.getTarget();
7583     ...
7584  }
7585  var myDiv = Roo.get("myDiv");
7586  myDiv.on("click", handleClick);
7587  //or
7588  Roo.EventManager.on("myDiv", 'click', handleClick);
7589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7590  </code></pre>
7591  * @static
7592  */
7593 Roo.EventObject = function(){
7594     
7595     var E = Roo.lib.Event;
7596     
7597     // safari keypress events for special keys return bad keycodes
7598     var safariKeys = {
7599         63234 : 37, // left
7600         63235 : 39, // right
7601         63232 : 38, // up
7602         63233 : 40, // down
7603         63276 : 33, // page up
7604         63277 : 34, // page down
7605         63272 : 46, // delete
7606         63273 : 36, // home
7607         63275 : 35  // end
7608     };
7609
7610     // normalize button clicks
7611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7613
7614     Roo.EventObjectImpl = function(e){
7615         if(e){
7616             this.setEvent(e.browserEvent || e);
7617         }
7618     };
7619     Roo.EventObjectImpl.prototype = {
7620         /**
7621          * Used to fix doc tools.
7622          * @scope Roo.EventObject.prototype
7623          */
7624             
7625
7626         
7627         
7628         /** The normal browser event */
7629         browserEvent : null,
7630         /** The button pressed in a mouse event */
7631         button : -1,
7632         /** True if the shift key was down during the event */
7633         shiftKey : false,
7634         /** True if the control key was down during the event */
7635         ctrlKey : false,
7636         /** True if the alt key was down during the event */
7637         altKey : false,
7638
7639         /** Key constant 
7640         * @type Number */
7641         BACKSPACE : 8,
7642         /** Key constant 
7643         * @type Number */
7644         TAB : 9,
7645         /** Key constant 
7646         * @type Number */
7647         RETURN : 13,
7648         /** Key constant 
7649         * @type Number */
7650         ENTER : 13,
7651         /** Key constant 
7652         * @type Number */
7653         SHIFT : 16,
7654         /** Key constant 
7655         * @type Number */
7656         CONTROL : 17,
7657         /** Key constant 
7658         * @type Number */
7659         ESC : 27,
7660         /** Key constant 
7661         * @type Number */
7662         SPACE : 32,
7663         /** Key constant 
7664         * @type Number */
7665         PAGEUP : 33,
7666         /** Key constant 
7667         * @type Number */
7668         PAGEDOWN : 34,
7669         /** Key constant 
7670         * @type Number */
7671         END : 35,
7672         /** Key constant 
7673         * @type Number */
7674         HOME : 36,
7675         /** Key constant 
7676         * @type Number */
7677         LEFT : 37,
7678         /** Key constant 
7679         * @type Number */
7680         UP : 38,
7681         /** Key constant 
7682         * @type Number */
7683         RIGHT : 39,
7684         /** Key constant 
7685         * @type Number */
7686         DOWN : 40,
7687         /** Key constant 
7688         * @type Number */
7689         DELETE : 46,
7690         /** Key constant 
7691         * @type Number */
7692         F5 : 116,
7693
7694            /** @private */
7695         setEvent : function(e){
7696             if(e == this || (e && e.browserEvent)){ // already wrapped
7697                 return e;
7698             }
7699             this.browserEvent = e;
7700             if(e){
7701                 // normalize buttons
7702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7703                 if(e.type == 'click' && this.button == -1){
7704                     this.button = 0;
7705                 }
7706                 this.type = e.type;
7707                 this.shiftKey = e.shiftKey;
7708                 // mac metaKey behaves like ctrlKey
7709                 this.ctrlKey = e.ctrlKey || e.metaKey;
7710                 this.altKey = e.altKey;
7711                 // in getKey these will be normalized for the mac
7712                 this.keyCode = e.keyCode;
7713                 // keyup warnings on firefox.
7714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7715                 // cache the target for the delayed and or buffered events
7716                 this.target = E.getTarget(e);
7717                 // same for XY
7718                 this.xy = E.getXY(e);
7719             }else{
7720                 this.button = -1;
7721                 this.shiftKey = false;
7722                 this.ctrlKey = false;
7723                 this.altKey = false;
7724                 this.keyCode = 0;
7725                 this.charCode =0;
7726                 this.target = null;
7727                 this.xy = [0, 0];
7728             }
7729             return this;
7730         },
7731
7732         /**
7733          * Stop the event (preventDefault and stopPropagation)
7734          */
7735         stopEvent : function(){
7736             if(this.browserEvent){
7737                 if(this.browserEvent.type == 'mousedown'){
7738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7739                 }
7740                 E.stopEvent(this.browserEvent);
7741             }
7742         },
7743
7744         /**
7745          * Prevents the browsers default handling of the event.
7746          */
7747         preventDefault : function(){
7748             if(this.browserEvent){
7749                 E.preventDefault(this.browserEvent);
7750             }
7751         },
7752
7753         /** @private */
7754         isNavKeyPress : function(){
7755             var k = this.keyCode;
7756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7758         },
7759
7760         isSpecialKey : function(){
7761             var k = this.keyCode;
7762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7763             (k == 16) || (k == 17) ||
7764             (k >= 18 && k <= 20) ||
7765             (k >= 33 && k <= 35) ||
7766             (k >= 36 && k <= 39) ||
7767             (k >= 44 && k <= 45);
7768         },
7769         /**
7770          * Cancels bubbling of the event.
7771          */
7772         stopPropagation : function(){
7773             if(this.browserEvent){
7774                 if(this.type == 'mousedown'){
7775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7776                 }
7777                 E.stopPropagation(this.browserEvent);
7778             }
7779         },
7780
7781         /**
7782          * Gets the key code for the event.
7783          * @return {Number}
7784          */
7785         getCharCode : function(){
7786             return this.charCode || this.keyCode;
7787         },
7788
7789         /**
7790          * Returns a normalized keyCode for the event.
7791          * @return {Number} The key code
7792          */
7793         getKey : function(){
7794             var k = this.keyCode || this.charCode;
7795             return Roo.isSafari ? (safariKeys[k] || k) : k;
7796         },
7797
7798         /**
7799          * Gets the x coordinate of the event.
7800          * @return {Number}
7801          */
7802         getPageX : function(){
7803             return this.xy[0];
7804         },
7805
7806         /**
7807          * Gets the y coordinate of the event.
7808          * @return {Number}
7809          */
7810         getPageY : function(){
7811             return this.xy[1];
7812         },
7813
7814         /**
7815          * Gets the time of the event.
7816          * @return {Number}
7817          */
7818         getTime : function(){
7819             if(this.browserEvent){
7820                 return E.getTime(this.browserEvent);
7821             }
7822             return null;
7823         },
7824
7825         /**
7826          * Gets the page coordinates of the event.
7827          * @return {Array} The xy values like [x, y]
7828          */
7829         getXY : function(){
7830             return this.xy;
7831         },
7832
7833         /**
7834          * Gets the target for the event.
7835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7837                 search as a number or element (defaults to 10 || document.body)
7838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7839          * @return {HTMLelement}
7840          */
7841         getTarget : function(selector, maxDepth, returnEl){
7842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7843         },
7844         /**
7845          * Gets the related target.
7846          * @return {HTMLElement}
7847          */
7848         getRelatedTarget : function(){
7849             if(this.browserEvent){
7850                 return E.getRelatedTarget(this.browserEvent);
7851             }
7852             return null;
7853         },
7854
7855         /**
7856          * Normalizes mouse wheel delta across browsers
7857          * @return {Number} The delta
7858          */
7859         getWheelDelta : function(){
7860             var e = this.browserEvent;
7861             var delta = 0;
7862             if(e.wheelDelta){ /* IE/Opera. */
7863                 delta = e.wheelDelta/120;
7864             }else if(e.detail){ /* Mozilla case. */
7865                 delta = -e.detail/3;
7866             }
7867             return delta;
7868         },
7869
7870         /**
7871          * Returns true if the control, meta, shift or alt key was pressed during this event.
7872          * @return {Boolean}
7873          */
7874         hasModifier : function(){
7875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7876         },
7877
7878         /**
7879          * Returns true if the target of this event equals el or is a child of el
7880          * @param {String/HTMLElement/Element} el
7881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7882          * @return {Boolean}
7883          */
7884         within : function(el, related){
7885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7886             return t && Roo.fly(el).contains(t);
7887         },
7888
7889         getPoint : function(){
7890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7891         }
7892     };
7893
7894     return new Roo.EventObjectImpl();
7895 }();
7896             
7897     /*
7898  * Based on:
7899  * Ext JS Library 1.1.1
7900  * Copyright(c) 2006-2007, Ext JS, LLC.
7901  *
7902  * Originally Released Under LGPL - original licence link has changed is not relivant.
7903  *
7904  * Fork - LGPL
7905  * <script type="text/javascript">
7906  */
7907
7908  
7909 // was in Composite Element!??!?!
7910  
7911 (function(){
7912     var D = Roo.lib.Dom;
7913     var E = Roo.lib.Event;
7914     var A = Roo.lib.Anim;
7915
7916     // local style camelizing for speed
7917     var propCache = {};
7918     var camelRe = /(-[a-z])/gi;
7919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7920     var view = document.defaultView;
7921
7922 /**
7923  * @class Roo.Element
7924  * Represents an Element in the DOM.<br><br>
7925  * Usage:<br>
7926 <pre><code>
7927 var el = Roo.get("my-div");
7928
7929 // or with getEl
7930 var el = getEl("my-div");
7931
7932 // or with a DOM element
7933 var el = Roo.get(myDivElement);
7934 </code></pre>
7935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7936  * each call instead of constructing a new one.<br><br>
7937  * <b>Animations</b><br />
7938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7940 <pre>
7941 Option    Default   Description
7942 --------- --------  ---------------------------------------------
7943 duration  .35       The duration of the animation in seconds
7944 easing    easeOut   The YUI easing method
7945 callback  none      A function to execute when the anim completes
7946 scope     this      The scope (this) of the callback function
7947 </pre>
7948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7949 * manipulate the animation. Here's an example:
7950 <pre><code>
7951 var el = Roo.get("my-div");
7952
7953 // no animation
7954 el.setWidth(100);
7955
7956 // default animation
7957 el.setWidth(100, true);
7958
7959 // animation with some options set
7960 el.setWidth(100, {
7961     duration: 1,
7962     callback: this.foo,
7963     scope: this
7964 });
7965
7966 // using the "anim" property to get the Anim object
7967 var opt = {
7968     duration: 1,
7969     callback: this.foo,
7970     scope: this
7971 };
7972 el.setWidth(100, opt);
7973 ...
7974 if(opt.anim.isAnimated()){
7975     opt.anim.stop();
7976 }
7977 </code></pre>
7978 * <b> Composite (Collections of) Elements</b><br />
7979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7980  * @constructor Create a new Element directly.
7981  * @param {String/HTMLElement} element
7982  * @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).
7983  */
7984     Roo.Element = function(element, forceNew)
7985     {
7986         var dom = typeof element == "string" ?
7987                 document.getElementById(element) : element;
7988         
7989         this.listeners = {};
7990         
7991         if(!dom){ // invalid id/element
7992             return null;
7993         }
7994         var id = dom.id;
7995         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7996             return Roo.Element.cache[id];
7997         }
7998
7999         /**
8000          * The DOM element
8001          * @type HTMLElement
8002          */
8003         this.dom = dom;
8004
8005         /**
8006          * The DOM element ID
8007          * @type String
8008          */
8009         this.id = id || Roo.id(dom);
8010         
8011         return this; // assumed for cctor?
8012     };
8013
8014     var El = Roo.Element;
8015
8016     El.prototype = {
8017         /**
8018          * The element's default display mode  (defaults to "") 
8019          * @type String
8020          */
8021         originalDisplay : "",
8022
8023         
8024         // note this is overridden in BS version..
8025         visibilityMode : 1, 
8026         /**
8027          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8028          * @type String
8029          */
8030         defaultUnit : "px",
8031         
8032         /**
8033          * Sets the element's visibility mode. When setVisible() is called it
8034          * will use this to determine whether to set the visibility or the display property.
8035          * @param visMode Element.VISIBILITY or Element.DISPLAY
8036          * @return {Roo.Element} this
8037          */
8038         setVisibilityMode : function(visMode){
8039             this.visibilityMode = visMode;
8040             return this;
8041         },
8042         /**
8043          * Convenience method for setVisibilityMode(Element.DISPLAY)
8044          * @param {String} display (optional) What to set display to when visible
8045          * @return {Roo.Element} this
8046          */
8047         enableDisplayMode : function(display){
8048             this.setVisibilityMode(El.DISPLAY);
8049             if(typeof display != "undefined") { this.originalDisplay = display; }
8050             return this;
8051         },
8052
8053         /**
8054          * 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)
8055          * @param {String} selector The simple selector to test
8056          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8057                 search as a number or element (defaults to 10 || document.body)
8058          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8059          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8060          */
8061         findParent : function(simpleSelector, maxDepth, returnEl){
8062             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8063             maxDepth = maxDepth || 50;
8064             if(typeof maxDepth != "number"){
8065                 stopEl = Roo.getDom(maxDepth);
8066                 maxDepth = 10;
8067             }
8068             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8069                 if(dq.is(p, simpleSelector)){
8070                     return returnEl ? Roo.get(p) : p;
8071                 }
8072                 depth++;
8073                 p = p.parentNode;
8074             }
8075             return null;
8076         },
8077
8078
8079         /**
8080          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8081          * @param {String} selector The simple selector to test
8082          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8083                 search as a number or element (defaults to 10 || document.body)
8084          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8085          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8086          */
8087         findParentNode : function(simpleSelector, maxDepth, returnEl){
8088             var p = Roo.fly(this.dom.parentNode, '_internal');
8089             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8090         },
8091         
8092         /**
8093          * Looks at  the scrollable parent element
8094          */
8095         findScrollableParent : function()
8096         {
8097             var overflowRegex = /(auto|scroll)/;
8098             
8099             if(this.getStyle('position') === 'fixed'){
8100                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8101             }
8102             
8103             var excludeStaticParent = this.getStyle('position') === "absolute";
8104             
8105             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8106                 
8107                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8108                     continue;
8109                 }
8110                 
8111                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8112                     return parent;
8113                 }
8114                 
8115                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8116                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8117                 }
8118             }
8119             
8120             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8121         },
8122
8123         /**
8124          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8125          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8126          * @param {String} selector The simple selector to test
8127          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8128                 search as a number or element (defaults to 10 || document.body)
8129          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8130          */
8131         up : function(simpleSelector, maxDepth){
8132             return this.findParentNode(simpleSelector, maxDepth, true);
8133         },
8134
8135
8136
8137         /**
8138          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8139          * @param {String} selector The simple selector to test
8140          * @return {Boolean} True if this element matches the selector, else false
8141          */
8142         is : function(simpleSelector){
8143             return Roo.DomQuery.is(this.dom, simpleSelector);
8144         },
8145
8146         /**
8147          * Perform animation on this element.
8148          * @param {Object} args The YUI animation control args
8149          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8150          * @param {Function} onComplete (optional) Function to call when animation completes
8151          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8152          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8153          * @return {Roo.Element} this
8154          */
8155         animate : function(args, duration, onComplete, easing, animType){
8156             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8157             return this;
8158         },
8159
8160         /*
8161          * @private Internal animation call
8162          */
8163         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8164             animType = animType || 'run';
8165             opt = opt || {};
8166             var anim = Roo.lib.Anim[animType](
8167                 this.dom, args,
8168                 (opt.duration || defaultDur) || .35,
8169                 (opt.easing || defaultEase) || 'easeOut',
8170                 function(){
8171                     Roo.callback(cb, this);
8172                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8173                 },
8174                 this
8175             );
8176             opt.anim = anim;
8177             return anim;
8178         },
8179
8180         // private legacy anim prep
8181         preanim : function(a, i){
8182             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8183         },
8184
8185         /**
8186          * Removes worthless text nodes
8187          * @param {Boolean} forceReclean (optional) By default the element
8188          * keeps track if it has been cleaned already so
8189          * you can call this over and over. However, if you update the element and
8190          * need to force a reclean, you can pass true.
8191          */
8192         clean : function(forceReclean){
8193             if(this.isCleaned && forceReclean !== true){
8194                 return this;
8195             }
8196             var ns = /\S/;
8197             var d = this.dom, n = d.firstChild, ni = -1;
8198             while(n){
8199                 var nx = n.nextSibling;
8200                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8201                     d.removeChild(n);
8202                 }else{
8203                     n.nodeIndex = ++ni;
8204                 }
8205                 n = nx;
8206             }
8207             this.isCleaned = true;
8208             return this;
8209         },
8210
8211         // private
8212         calcOffsetsTo : function(el){
8213             el = Roo.get(el);
8214             var d = el.dom;
8215             var restorePos = false;
8216             if(el.getStyle('position') == 'static'){
8217                 el.position('relative');
8218                 restorePos = true;
8219             }
8220             var x = 0, y =0;
8221             var op = this.dom;
8222             while(op && op != d && op.tagName != 'HTML'){
8223                 x+= op.offsetLeft;
8224                 y+= op.offsetTop;
8225                 op = op.offsetParent;
8226             }
8227             if(restorePos){
8228                 el.position('static');
8229             }
8230             return [x, y];
8231         },
8232
8233         /**
8234          * Scrolls this element into view within the passed container.
8235          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8236          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8237          * @return {Roo.Element} this
8238          */
8239         scrollIntoView : function(container, hscroll){
8240             var c = Roo.getDom(container) || document.body;
8241             var el = this.dom;
8242
8243             var o = this.calcOffsetsTo(c),
8244                 l = o[0],
8245                 t = o[1],
8246                 b = t+el.offsetHeight,
8247                 r = l+el.offsetWidth;
8248
8249             var ch = c.clientHeight;
8250             var ct = parseInt(c.scrollTop, 10);
8251             var cl = parseInt(c.scrollLeft, 10);
8252             var cb = ct + ch;
8253             var cr = cl + c.clientWidth;
8254
8255             if(t < ct){
8256                 c.scrollTop = t;
8257             }else if(b > cb){
8258                 c.scrollTop = b-ch;
8259             }
8260
8261             if(hscroll !== false){
8262                 if(l < cl){
8263                     c.scrollLeft = l;
8264                 }else if(r > cr){
8265                     c.scrollLeft = r-c.clientWidth;
8266                 }
8267             }
8268             return this;
8269         },
8270
8271         // private
8272         scrollChildIntoView : function(child, hscroll){
8273             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8274         },
8275
8276         /**
8277          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8278          * the new height may not be available immediately.
8279          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8280          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8281          * @param {Function} onComplete (optional) Function to call when animation completes
8282          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8283          * @return {Roo.Element} this
8284          */
8285         autoHeight : function(animate, duration, onComplete, easing){
8286             var oldHeight = this.getHeight();
8287             this.clip();
8288             this.setHeight(1); // force clipping
8289             setTimeout(function(){
8290                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8291                 if(!animate){
8292                     this.setHeight(height);
8293                     this.unclip();
8294                     if(typeof onComplete == "function"){
8295                         onComplete();
8296                     }
8297                 }else{
8298                     this.setHeight(oldHeight); // restore original height
8299                     this.setHeight(height, animate, duration, function(){
8300                         this.unclip();
8301                         if(typeof onComplete == "function") { onComplete(); }
8302                     }.createDelegate(this), easing);
8303                 }
8304             }.createDelegate(this), 0);
8305             return this;
8306         },
8307
8308         /**
8309          * Returns true if this element is an ancestor of the passed element
8310          * @param {HTMLElement/String} el The element to check
8311          * @return {Boolean} True if this element is an ancestor of el, else false
8312          */
8313         contains : function(el){
8314             if(!el){return false;}
8315             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8316         },
8317
8318         /**
8319          * Checks whether the element is currently visible using both visibility and display properties.
8320          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8321          * @return {Boolean} True if the element is currently visible, else false
8322          */
8323         isVisible : function(deep) {
8324             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8325             if(deep !== true || !vis){
8326                 return vis;
8327             }
8328             var p = this.dom.parentNode;
8329             while(p && p.tagName.toLowerCase() != "body"){
8330                 if(!Roo.fly(p, '_isVisible').isVisible()){
8331                     return false;
8332                 }
8333                 p = p.parentNode;
8334             }
8335             return true;
8336         },
8337
8338         /**
8339          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8342          * @return {CompositeElement/CompositeElementLite} The composite element
8343          */
8344         select : function(selector, unique){
8345             return El.select(selector, unique, this.dom);
8346         },
8347
8348         /**
8349          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8350          * @param {String} selector The CSS selector
8351          * @return {Array} An array of the matched nodes
8352          */
8353         query : function(selector, unique){
8354             return Roo.DomQuery.select(selector, this.dom);
8355         },
8356
8357         /**
8358          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8359          * @param {String} selector The CSS selector
8360          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8361          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8362          */
8363         child : function(selector, returnDom){
8364             var n = Roo.DomQuery.selectNode(selector, this.dom);
8365             return returnDom ? n : Roo.get(n);
8366         },
8367
8368         /**
8369          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8370          * @param {String} selector The CSS selector
8371          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8372          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8373          */
8374         down : function(selector, returnDom){
8375             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8376             return returnDom ? n : Roo.get(n);
8377         },
8378
8379         /**
8380          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8381          * @param {String} group The group the DD object is member of
8382          * @param {Object} config The DD config object
8383          * @param {Object} overrides An object containing methods to override/implement on the DD object
8384          * @return {Roo.dd.DD} The DD object
8385          */
8386         initDD : function(group, config, overrides){
8387             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8388             return Roo.apply(dd, overrides);
8389         },
8390
8391         /**
8392          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8393          * @param {String} group The group the DDProxy object is member of
8394          * @param {Object} config The DDProxy config object
8395          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8396          * @return {Roo.dd.DDProxy} The DDProxy object
8397          */
8398         initDDProxy : function(group, config, overrides){
8399             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8400             return Roo.apply(dd, overrides);
8401         },
8402
8403         /**
8404          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8405          * @param {String} group The group the DDTarget object is member of
8406          * @param {Object} config The DDTarget config object
8407          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8408          * @return {Roo.dd.DDTarget} The DDTarget object
8409          */
8410         initDDTarget : function(group, config, overrides){
8411             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8412             return Roo.apply(dd, overrides);
8413         },
8414
8415         /**
8416          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8417          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8418          * @param {Boolean} visible Whether the element is visible
8419          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8420          * @return {Roo.Element} this
8421          */
8422          setVisible : function(visible, animate){
8423             if(!animate || !A){
8424                 if(this.visibilityMode == El.DISPLAY){
8425                     this.setDisplayed(visible);
8426                 }else{
8427                     this.fixDisplay();
8428                     this.dom.style.visibility = visible ? "visible" : "hidden";
8429                 }
8430             }else{
8431                 // closure for composites
8432                 var dom = this.dom;
8433                 var visMode = this.visibilityMode;
8434                 if(visible){
8435                     this.setOpacity(.01);
8436                     this.setVisible(true);
8437                 }
8438                 this.anim({opacity: { to: (visible?1:0) }},
8439                       this.preanim(arguments, 1),
8440                       null, .35, 'easeIn', function(){
8441                          if(!visible){
8442                              if(visMode == El.DISPLAY){
8443                                  dom.style.display = "none";
8444                              }else{
8445                                  dom.style.visibility = "hidden";
8446                              }
8447                              Roo.get(dom).setOpacity(1);
8448                          }
8449                      });
8450             }
8451             return this;
8452         },
8453
8454         /**
8455          * Returns true if display is not "none"
8456          * @return {Boolean}
8457          */
8458         isDisplayed : function() {
8459             return this.getStyle("display") != "none";
8460         },
8461
8462         /**
8463          * Toggles the element's visibility or display, depending on visibility mode.
8464          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8465          * @return {Roo.Element} this
8466          */
8467         toggle : function(animate){
8468             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8469             return this;
8470         },
8471
8472         /**
8473          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8474          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8475          * @return {Roo.Element} this
8476          */
8477         setDisplayed : function(value) {
8478             if(typeof value == "boolean"){
8479                value = value ? this.originalDisplay : "none";
8480             }
8481             this.setStyle("display", value);
8482             return this;
8483         },
8484
8485         /**
8486          * Tries to focus the element. Any exceptions are caught and ignored.
8487          * @return {Roo.Element} this
8488          */
8489         focus : function() {
8490             try{
8491                 this.dom.focus();
8492             }catch(e){}
8493             return this;
8494         },
8495
8496         /**
8497          * Tries to blur the element. Any exceptions are caught and ignored.
8498          * @return {Roo.Element} this
8499          */
8500         blur : function() {
8501             try{
8502                 this.dom.blur();
8503             }catch(e){}
8504             return this;
8505         },
8506
8507         /**
8508          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8509          * @param {String/Array} className The CSS class to add, or an array of classes
8510          * @return {Roo.Element} this
8511          */
8512         addClass : function(className){
8513             if(className instanceof Array){
8514                 for(var i = 0, len = className.length; i < len; i++) {
8515                     this.addClass(className[i]);
8516                 }
8517             }else{
8518                 if(className && !this.hasClass(className)){
8519                     if (this.dom instanceof SVGElement) {
8520                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8521                     } else {
8522                         this.dom.className = this.dom.className + " " + className;
8523                     }
8524                 }
8525             }
8526             return this;
8527         },
8528
8529         /**
8530          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8531          * @param {String/Array} className The CSS class to add, or an array of classes
8532          * @return {Roo.Element} this
8533          */
8534         radioClass : function(className){
8535             var siblings = this.dom.parentNode.childNodes;
8536             for(var i = 0; i < siblings.length; i++) {
8537                 var s = siblings[i];
8538                 if(s.nodeType == 1){
8539                     Roo.get(s).removeClass(className);
8540                 }
8541             }
8542             this.addClass(className);
8543             return this;
8544         },
8545
8546         /**
8547          * Removes one or more CSS classes from the element.
8548          * @param {String/Array} className The CSS class to remove, or an array of classes
8549          * @return {Roo.Element} this
8550          */
8551         removeClass : function(className){
8552             
8553             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8554             if(!className || !cn){
8555                 return this;
8556             }
8557             if(className instanceof Array){
8558                 for(var i = 0, len = className.length; i < len; i++) {
8559                     this.removeClass(className[i]);
8560                 }
8561             }else{
8562                 if(this.hasClass(className)){
8563                     var re = this.classReCache[className];
8564                     if (!re) {
8565                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8566                        this.classReCache[className] = re;
8567                     }
8568                     if (this.dom instanceof SVGElement) {
8569                         this.dom.className.baseVal = cn.replace(re, " ");
8570                     } else {
8571                         this.dom.className = cn.replace(re, " ");
8572                     }
8573                 }
8574             }
8575             return this;
8576         },
8577
8578         // private
8579         classReCache: {},
8580
8581         /**
8582          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8583          * @param {String} className The CSS class to toggle
8584          * @return {Roo.Element} this
8585          */
8586         toggleClass : function(className){
8587             if(this.hasClass(className)){
8588                 this.removeClass(className);
8589             }else{
8590                 this.addClass(className);
8591             }
8592             return this;
8593         },
8594
8595         /**
8596          * Checks if the specified CSS class exists on this element's DOM node.
8597          * @param {String} className The CSS class to check for
8598          * @return {Boolean} True if the class exists, else false
8599          */
8600         hasClass : function(className){
8601             if (this.dom instanceof SVGElement) {
8602                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8603             } 
8604             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8605         },
8606
8607         /**
8608          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8609          * @param {String} oldClassName The CSS class to replace
8610          * @param {String} newClassName The replacement CSS class
8611          * @return {Roo.Element} this
8612          */
8613         replaceClass : function(oldClassName, newClassName){
8614             this.removeClass(oldClassName);
8615             this.addClass(newClassName);
8616             return this;
8617         },
8618
8619         /**
8620          * Returns an object with properties matching the styles requested.
8621          * For example, el.getStyles('color', 'font-size', 'width') might return
8622          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8623          * @param {String} style1 A style name
8624          * @param {String} style2 A style name
8625          * @param {String} etc.
8626          * @return {Object} The style object
8627          */
8628         getStyles : function(){
8629             var a = arguments, len = a.length, r = {};
8630             for(var i = 0; i < len; i++){
8631                 r[a[i]] = this.getStyle(a[i]);
8632             }
8633             return r;
8634         },
8635
8636         /**
8637          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8638          * @param {String} property The style property whose value is returned.
8639          * @return {String} The current value of the style property for this element.
8640          */
8641         getStyle : function(){
8642             return view && view.getComputedStyle ?
8643                 function(prop){
8644                     var el = this.dom, v, cs, camel;
8645                     if(prop == 'float'){
8646                         prop = "cssFloat";
8647                     }
8648                     if(el.style && (v = el.style[prop])){
8649                         return v;
8650                     }
8651                     if(cs = view.getComputedStyle(el, "")){
8652                         if(!(camel = propCache[prop])){
8653                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8654                         }
8655                         return cs[camel];
8656                     }
8657                     return null;
8658                 } :
8659                 function(prop){
8660                     var el = this.dom, v, cs, camel;
8661                     if(prop == 'opacity'){
8662                         if(typeof el.style.filter == 'string'){
8663                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8664                             if(m){
8665                                 var fv = parseFloat(m[1]);
8666                                 if(!isNaN(fv)){
8667                                     return fv ? fv / 100 : 0;
8668                                 }
8669                             }
8670                         }
8671                         return 1;
8672                     }else if(prop == 'float'){
8673                         prop = "styleFloat";
8674                     }
8675                     if(!(camel = propCache[prop])){
8676                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8677                     }
8678                     if(v = el.style[camel]){
8679                         return v;
8680                     }
8681                     if(cs = el.currentStyle){
8682                         return cs[camel];
8683                     }
8684                     return null;
8685                 };
8686         }(),
8687
8688         /**
8689          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8690          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8691          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8692          * @return {Roo.Element} this
8693          */
8694         setStyle : function(prop, value){
8695             if(typeof prop == "string"){
8696                 
8697                 if (prop == 'float') {
8698                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8699                     return this;
8700                 }
8701                 
8702                 var camel;
8703                 if(!(camel = propCache[prop])){
8704                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8705                 }
8706                 
8707                 if(camel == 'opacity') {
8708                     this.setOpacity(value);
8709                 }else{
8710                     this.dom.style[camel] = value;
8711                 }
8712             }else{
8713                 for(var style in prop){
8714                     if(typeof prop[style] != "function"){
8715                        this.setStyle(style, prop[style]);
8716                     }
8717                 }
8718             }
8719             return this;
8720         },
8721
8722         /**
8723          * More flexible version of {@link #setStyle} for setting style properties.
8724          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8725          * a function which returns such a specification.
8726          * @return {Roo.Element} this
8727          */
8728         applyStyles : function(style){
8729             Roo.DomHelper.applyStyles(this.dom, style);
8730             return this;
8731         },
8732
8733         /**
8734           * 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).
8735           * @return {Number} The X position of the element
8736           */
8737         getX : function(){
8738             return D.getX(this.dom);
8739         },
8740
8741         /**
8742           * 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).
8743           * @return {Number} The Y position of the element
8744           */
8745         getY : function(){
8746             return D.getY(this.dom);
8747         },
8748
8749         /**
8750           * 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).
8751           * @return {Array} The XY position of the element
8752           */
8753         getXY : function(){
8754             return D.getXY(this.dom);
8755         },
8756
8757         /**
8758          * 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).
8759          * @param {Number} The X position of the element
8760          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8761          * @return {Roo.Element} this
8762          */
8763         setX : function(x, animate){
8764             if(!animate || !A){
8765                 D.setX(this.dom, x);
8766             }else{
8767                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8768             }
8769             return this;
8770         },
8771
8772         /**
8773          * 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).
8774          * @param {Number} The Y position of the element
8775          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8776          * @return {Roo.Element} this
8777          */
8778         setY : function(y, animate){
8779             if(!animate || !A){
8780                 D.setY(this.dom, y);
8781             }else{
8782                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8783             }
8784             return this;
8785         },
8786
8787         /**
8788          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8789          * @param {String} left The left CSS property value
8790          * @return {Roo.Element} this
8791          */
8792         setLeft : function(left){
8793             this.setStyle("left", this.addUnits(left));
8794             return this;
8795         },
8796
8797         /**
8798          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8799          * @param {String} top The top CSS property value
8800          * @return {Roo.Element} this
8801          */
8802         setTop : function(top){
8803             this.setStyle("top", this.addUnits(top));
8804             return this;
8805         },
8806
8807         /**
8808          * Sets the element's CSS right style.
8809          * @param {String} right The right CSS property value
8810          * @return {Roo.Element} this
8811          */
8812         setRight : function(right){
8813             this.setStyle("right", this.addUnits(right));
8814             return this;
8815         },
8816
8817         /**
8818          * Sets the element's CSS bottom style.
8819          * @param {String} bottom The bottom CSS property value
8820          * @return {Roo.Element} this
8821          */
8822         setBottom : function(bottom){
8823             this.setStyle("bottom", this.addUnits(bottom));
8824             return this;
8825         },
8826
8827         /**
8828          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8829          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8830          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8831          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8832          * @return {Roo.Element} this
8833          */
8834         setXY : function(pos, animate){
8835             if(!animate || !A){
8836                 D.setXY(this.dom, pos);
8837             }else{
8838                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8839             }
8840             return this;
8841         },
8842
8843         /**
8844          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8845          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8846          * @param {Number} x X value for new position (coordinates are page-based)
8847          * @param {Number} y Y value for new position (coordinates are page-based)
8848          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8849          * @return {Roo.Element} this
8850          */
8851         setLocation : function(x, y, animate){
8852             this.setXY([x, y], this.preanim(arguments, 2));
8853             return this;
8854         },
8855
8856         /**
8857          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8858          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8859          * @param {Number} x X value for new position (coordinates are page-based)
8860          * @param {Number} y Y value for new position (coordinates are page-based)
8861          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8862          * @return {Roo.Element} this
8863          */
8864         moveTo : function(x, y, animate){
8865             this.setXY([x, y], this.preanim(arguments, 2));
8866             return this;
8867         },
8868
8869         /**
8870          * Returns the region of the given element.
8871          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8872          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8873          */
8874         getRegion : function(){
8875             return D.getRegion(this.dom);
8876         },
8877
8878         /**
8879          * Returns the offset height of the element
8880          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8881          * @return {Number} The element's height
8882          */
8883         getHeight : function(contentHeight){
8884             var h = this.dom.offsetHeight || 0;
8885             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8886         },
8887
8888         /**
8889          * Returns the offset width of the element
8890          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8891          * @return {Number} The element's width
8892          */
8893         getWidth : function(contentWidth){
8894             var w = this.dom.offsetWidth || 0;
8895             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8896         },
8897
8898         /**
8899          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8900          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8901          * if a height has not been set using CSS.
8902          * @return {Number}
8903          */
8904         getComputedHeight : function(){
8905             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8906             if(!h){
8907                 h = parseInt(this.getStyle('height'), 10) || 0;
8908                 if(!this.isBorderBox()){
8909                     h += this.getFrameWidth('tb');
8910                 }
8911             }
8912             return h;
8913         },
8914
8915         /**
8916          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8917          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8918          * if a width has not been set using CSS.
8919          * @return {Number}
8920          */
8921         getComputedWidth : function(){
8922             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8923             if(!w){
8924                 w = parseInt(this.getStyle('width'), 10) || 0;
8925                 if(!this.isBorderBox()){
8926                     w += this.getFrameWidth('lr');
8927                 }
8928             }
8929             return w;
8930         },
8931
8932         /**
8933          * Returns the size of the element.
8934          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8935          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8936          */
8937         getSize : function(contentSize){
8938             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8939         },
8940
8941         /**
8942          * Returns the width and height of the viewport.
8943          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8944          */
8945         getViewSize : function(){
8946             var d = this.dom, doc = document, aw = 0, ah = 0;
8947             if(d == doc || d == doc.body){
8948                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8949             }else{
8950                 return {
8951                     width : d.clientWidth,
8952                     height: d.clientHeight
8953                 };
8954             }
8955         },
8956
8957         /**
8958          * Returns the value of the "value" attribute
8959          * @param {Boolean} asNumber true to parse the value as a number
8960          * @return {String/Number}
8961          */
8962         getValue : function(asNumber){
8963             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8964         },
8965
8966         // private
8967         adjustWidth : function(width){
8968             if(typeof width == "number"){
8969                 if(this.autoBoxAdjust && !this.isBorderBox()){
8970                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8971                 }
8972                 if(width < 0){
8973                     width = 0;
8974                 }
8975             }
8976             return width;
8977         },
8978
8979         // private
8980         adjustHeight : function(height){
8981             if(typeof height == "number"){
8982                if(this.autoBoxAdjust && !this.isBorderBox()){
8983                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8984                }
8985                if(height < 0){
8986                    height = 0;
8987                }
8988             }
8989             return height;
8990         },
8991
8992         /**
8993          * Set the width of the element
8994          * @param {Number} width The new width
8995          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8996          * @return {Roo.Element} this
8997          */
8998         setWidth : function(width, animate){
8999             width = this.adjustWidth(width);
9000             if(!animate || !A){
9001                 this.dom.style.width = this.addUnits(width);
9002             }else{
9003                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9004             }
9005             return this;
9006         },
9007
9008         /**
9009          * Set the height of the element
9010          * @param {Number} height The new height
9011          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9012          * @return {Roo.Element} this
9013          */
9014          setHeight : function(height, animate){
9015             height = this.adjustHeight(height);
9016             if(!animate || !A){
9017                 this.dom.style.height = this.addUnits(height);
9018             }else{
9019                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9020             }
9021             return this;
9022         },
9023
9024         /**
9025          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9026          * @param {Number} width The new width
9027          * @param {Number} height The new height
9028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9029          * @return {Roo.Element} this
9030          */
9031          setSize : function(width, height, animate){
9032             if(typeof width == "object"){ // in case of object from getSize()
9033                 height = width.height; width = width.width;
9034             }
9035             width = this.adjustWidth(width); height = this.adjustHeight(height);
9036             if(!animate || !A){
9037                 this.dom.style.width = this.addUnits(width);
9038                 this.dom.style.height = this.addUnits(height);
9039             }else{
9040                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9041             }
9042             return this;
9043         },
9044
9045         /**
9046          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9047          * @param {Number} x X value for new position (coordinates are page-based)
9048          * @param {Number} y Y value for new position (coordinates are page-based)
9049          * @param {Number} width The new width
9050          * @param {Number} height The new height
9051          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9052          * @return {Roo.Element} this
9053          */
9054         setBounds : function(x, y, width, height, animate){
9055             if(!animate || !A){
9056                 this.setSize(width, height);
9057                 this.setLocation(x, y);
9058             }else{
9059                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9060                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9061                               this.preanim(arguments, 4), 'motion');
9062             }
9063             return this;
9064         },
9065
9066         /**
9067          * 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.
9068          * @param {Roo.lib.Region} region The region to fill
9069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9070          * @return {Roo.Element} this
9071          */
9072         setRegion : function(region, animate){
9073             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9074             return this;
9075         },
9076
9077         /**
9078          * Appends an event handler
9079          *
9080          * @param {String}   eventName     The type of event to append
9081          * @param {Function} fn        The method the event invokes
9082          * @param {Object} scope       (optional) The scope (this object) of the fn
9083          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9084          */
9085         addListener : function(eventName, fn, scope, options)
9086         {
9087             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9088                 this.addListener('touchstart', this.onTapHandler, this);
9089             }
9090             
9091             // we need to handle a special case where dom element is a svg element.
9092             // in this case we do not actua
9093             if (!this.dom) {
9094                 return;
9095             }
9096             
9097             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9098                 if (typeof(this.listeners[eventName]) == 'undefined') {
9099                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9100                 }
9101                 this.listeners[eventName].addListener(fn, scope, options);
9102                 return;
9103             }
9104             
9105                 
9106             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9107             
9108             
9109         },
9110         tapedTwice : false,
9111         onTapHandler : function(event)
9112         {
9113             if(!this.tapedTwice) {
9114                 this.tapedTwice = true;
9115                 var s = this;
9116                 setTimeout( function() {
9117                     s.tapedTwice = false;
9118                 }, 300 );
9119                 return;
9120             }
9121             event.preventDefault();
9122             var revent = new MouseEvent('dblclick',  {
9123                 view: window,
9124                 bubbles: true,
9125                 cancelable: true
9126             });
9127              
9128             this.dom.dispatchEvent(revent);
9129             //action on double tap goes below
9130              
9131         }, 
9132  
9133         /**
9134          * Removes an event handler from this element
9135          * @param {String} eventName the type of event to remove
9136          * @param {Function} fn the method the event invokes
9137          * @param {Function} scope (needed for svg fake listeners)
9138          * @return {Roo.Element} this
9139          */
9140         removeListener : function(eventName, fn, scope){
9141             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9142             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9143                 return this;
9144             }
9145             this.listeners[eventName].removeListener(fn, scope);
9146             return this;
9147         },
9148
9149         /**
9150          * Removes all previous added listeners from this element
9151          * @return {Roo.Element} this
9152          */
9153         removeAllListeners : function(){
9154             E.purgeElement(this.dom);
9155             this.listeners = {};
9156             return this;
9157         },
9158
9159         relayEvent : function(eventName, observable){
9160             this.on(eventName, function(e){
9161                 observable.fireEvent(eventName, e);
9162             });
9163         },
9164
9165         
9166         /**
9167          * Set the opacity of the element
9168          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9170          * @return {Roo.Element} this
9171          */
9172          setOpacity : function(opacity, animate){
9173             if(!animate || !A){
9174                 var s = this.dom.style;
9175                 if(Roo.isIE){
9176                     s.zoom = 1;
9177                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9178                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9179                 }else{
9180                     s.opacity = opacity;
9181                 }
9182             }else{
9183                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Gets the left X coordinate
9190          * @param {Boolean} local True to get the local css position instead of page coordinate
9191          * @return {Number}
9192          */
9193         getLeft : function(local){
9194             if(!local){
9195                 return this.getX();
9196             }else{
9197                 return parseInt(this.getStyle("left"), 10) || 0;
9198             }
9199         },
9200
9201         /**
9202          * Gets the right X coordinate of the element (element X position + element width)
9203          * @param {Boolean} local True to get the local css position instead of page coordinate
9204          * @return {Number}
9205          */
9206         getRight : function(local){
9207             if(!local){
9208                 return this.getX() + this.getWidth();
9209             }else{
9210                 return (this.getLeft(true) + this.getWidth()) || 0;
9211             }
9212         },
9213
9214         /**
9215          * Gets the top Y coordinate
9216          * @param {Boolean} local True to get the local css position instead of page coordinate
9217          * @return {Number}
9218          */
9219         getTop : function(local) {
9220             if(!local){
9221                 return this.getY();
9222             }else{
9223                 return parseInt(this.getStyle("top"), 10) || 0;
9224             }
9225         },
9226
9227         /**
9228          * Gets the bottom Y coordinate of the element (element Y position + element height)
9229          * @param {Boolean} local True to get the local css position instead of page coordinate
9230          * @return {Number}
9231          */
9232         getBottom : function(local){
9233             if(!local){
9234                 return this.getY() + this.getHeight();
9235             }else{
9236                 return (this.getTop(true) + this.getHeight()) || 0;
9237             }
9238         },
9239
9240         /**
9241         * Initializes positioning on this element. If a desired position is not passed, it will make the
9242         * the element positioned relative IF it is not already positioned.
9243         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9244         * @param {Number} zIndex (optional) The zIndex to apply
9245         * @param {Number} x (optional) Set the page X position
9246         * @param {Number} y (optional) Set the page Y position
9247         */
9248         position : function(pos, zIndex, x, y){
9249             if(!pos){
9250                if(this.getStyle('position') == 'static'){
9251                    this.setStyle('position', 'relative');
9252                }
9253             }else{
9254                 this.setStyle("position", pos);
9255             }
9256             if(zIndex){
9257                 this.setStyle("z-index", zIndex);
9258             }
9259             if(x !== undefined && y !== undefined){
9260                 this.setXY([x, y]);
9261             }else if(x !== undefined){
9262                 this.setX(x);
9263             }else if(y !== undefined){
9264                 this.setY(y);
9265             }
9266         },
9267
9268         /**
9269         * Clear positioning back to the default when the document was loaded
9270         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9271         * @return {Roo.Element} this
9272          */
9273         clearPositioning : function(value){
9274             value = value ||'';
9275             this.setStyle({
9276                 "left": value,
9277                 "right": value,
9278                 "top": value,
9279                 "bottom": value,
9280                 "z-index": "",
9281                 "position" : "static"
9282             });
9283             return this;
9284         },
9285
9286         /**
9287         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9288         * snapshot before performing an update and then restoring the element.
9289         * @return {Object}
9290         */
9291         getPositioning : function(){
9292             var l = this.getStyle("left");
9293             var t = this.getStyle("top");
9294             return {
9295                 "position" : this.getStyle("position"),
9296                 "left" : l,
9297                 "right" : l ? "" : this.getStyle("right"),
9298                 "top" : t,
9299                 "bottom" : t ? "" : this.getStyle("bottom"),
9300                 "z-index" : this.getStyle("z-index")
9301             };
9302         },
9303
9304         /**
9305          * Gets the width of the border(s) for the specified side(s)
9306          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9307          * passing lr would get the border (l)eft width + the border (r)ight width.
9308          * @return {Number} The width of the sides passed added together
9309          */
9310         getBorderWidth : function(side){
9311             return this.addStyles(side, El.borders);
9312         },
9313
9314         /**
9315          * Gets the width of the padding(s) for the specified side(s)
9316          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9317          * passing lr would get the padding (l)eft + the padding (r)ight.
9318          * @return {Number} The padding of the sides passed added together
9319          */
9320         getPadding : function(side){
9321             return this.addStyles(side, El.paddings);
9322         },
9323
9324         /**
9325         * Set positioning with an object returned by getPositioning().
9326         * @param {Object} posCfg
9327         * @return {Roo.Element} this
9328          */
9329         setPositioning : function(pc){
9330             this.applyStyles(pc);
9331             if(pc.right == "auto"){
9332                 this.dom.style.right = "";
9333             }
9334             if(pc.bottom == "auto"){
9335                 this.dom.style.bottom = "";
9336             }
9337             return this;
9338         },
9339
9340         // private
9341         fixDisplay : function(){
9342             if(this.getStyle("display") == "none"){
9343                 this.setStyle("visibility", "hidden");
9344                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9345                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9346                     this.setStyle("display", "block");
9347                 }
9348             }
9349         },
9350
9351         /**
9352          * Quick set left and top adding default units
9353          * @param {String} left The left CSS property value
9354          * @param {String} top The top CSS property value
9355          * @return {Roo.Element} this
9356          */
9357          setLeftTop : function(left, top){
9358             this.dom.style.left = this.addUnits(left);
9359             this.dom.style.top = this.addUnits(top);
9360             return this;
9361         },
9362
9363         /**
9364          * Move this element relative to its current position.
9365          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9366          * @param {Number} distance How far to move the element in pixels
9367          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9368          * @return {Roo.Element} this
9369          */
9370          move : function(direction, distance, animate){
9371             var xy = this.getXY();
9372             direction = direction.toLowerCase();
9373             switch(direction){
9374                 case "l":
9375                 case "left":
9376                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9377                     break;
9378                case "r":
9379                case "right":
9380                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9381                     break;
9382                case "t":
9383                case "top":
9384                case "up":
9385                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9386                     break;
9387                case "b":
9388                case "bottom":
9389                case "down":
9390                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9391                     break;
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9398          * @return {Roo.Element} this
9399          */
9400         clip : function(){
9401             if(!this.isClipped){
9402                this.isClipped = true;
9403                this.originalClip = {
9404                    "o": this.getStyle("overflow"),
9405                    "x": this.getStyle("overflow-x"),
9406                    "y": this.getStyle("overflow-y")
9407                };
9408                this.setStyle("overflow", "hidden");
9409                this.setStyle("overflow-x", "hidden");
9410                this.setStyle("overflow-y", "hidden");
9411             }
9412             return this;
9413         },
9414
9415         /**
9416          *  Return clipping (overflow) to original clipping before clip() was called
9417          * @return {Roo.Element} this
9418          */
9419         unclip : function(){
9420             if(this.isClipped){
9421                 this.isClipped = false;
9422                 var o = this.originalClip;
9423                 if(o.o){this.setStyle("overflow", o.o);}
9424                 if(o.x){this.setStyle("overflow-x", o.x);}
9425                 if(o.y){this.setStyle("overflow-y", o.y);}
9426             }
9427             return this;
9428         },
9429
9430
9431         /**
9432          * Gets the x,y coordinates specified by the anchor position on the element.
9433          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9434          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9435          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9436          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9437          * @return {Array} [x, y] An array containing the element's x and y coordinates
9438          */
9439         getAnchorXY : function(anchor, local, s){
9440             //Passing a different size is useful for pre-calculating anchors,
9441             //especially for anchored animations that change the el size.
9442
9443             var w, h, vp = false;
9444             if(!s){
9445                 var d = this.dom;
9446                 if(d == document.body || d == document){
9447                     vp = true;
9448                     w = D.getViewWidth(); h = D.getViewHeight();
9449                 }else{
9450                     w = this.getWidth(); h = this.getHeight();
9451                 }
9452             }else{
9453                 w = s.width;  h = s.height;
9454             }
9455             var x = 0, y = 0, r = Math.round;
9456             switch((anchor || "tl").toLowerCase()){
9457                 case "c":
9458                     x = r(w*.5);
9459                     y = r(h*.5);
9460                 break;
9461                 case "t":
9462                     x = r(w*.5);
9463                     y = 0;
9464                 break;
9465                 case "l":
9466                     x = 0;
9467                     y = r(h*.5);
9468                 break;
9469                 case "r":
9470                     x = w;
9471                     y = r(h*.5);
9472                 break;
9473                 case "b":
9474                     x = r(w*.5);
9475                     y = h;
9476                 break;
9477                 case "tl":
9478                     x = 0;
9479                     y = 0;
9480                 break;
9481                 case "bl":
9482                     x = 0;
9483                     y = h;
9484                 break;
9485                 case "br":
9486                     x = w;
9487                     y = h;
9488                 break;
9489                 case "tr":
9490                     x = w;
9491                     y = 0;
9492                 break;
9493             }
9494             if(local === true){
9495                 return [x, y];
9496             }
9497             if(vp){
9498                 var sc = this.getScroll();
9499                 return [x + sc.left, y + sc.top];
9500             }
9501             //Add the element's offset xy
9502             var o = this.getXY();
9503             return [x+o[0], y+o[1]];
9504         },
9505
9506         /**
9507          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9508          * supported position values.
9509          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9510          * @param {String} position The position to align to.
9511          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9512          * @return {Array} [x, y]
9513          */
9514         getAlignToXY : function(el, p, o)
9515         {
9516             el = Roo.get(el);
9517             var d = this.dom;
9518             if(!el.dom){
9519                 throw "Element.alignTo with an element that doesn't exist";
9520             }
9521             var c = false; //constrain to viewport
9522             var p1 = "", p2 = "";
9523             o = o || [0,0];
9524
9525             if(!p){
9526                 p = "tl-bl";
9527             }else if(p == "?"){
9528                 p = "tl-bl?";
9529             }else if(p.indexOf("-") == -1){
9530                 p = "tl-" + p;
9531             }
9532             p = p.toLowerCase();
9533             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9534             if(!m){
9535                throw "Element.alignTo with an invalid alignment " + p;
9536             }
9537             p1 = m[1]; p2 = m[2]; c = !!m[3];
9538
9539             //Subtract the aligned el's internal xy from the target's offset xy
9540             //plus custom offset to get the aligned el's new offset xy
9541             var a1 = this.getAnchorXY(p1, true);
9542             var a2 = el.getAnchorXY(p2, false);
9543             var x = a2[0] - a1[0] + o[0];
9544             var y = a2[1] - a1[1] + o[1];
9545             if(c){
9546                 //constrain the aligned el to viewport if necessary
9547                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9548                 // 5px of margin for ie
9549                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9550
9551                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9552                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9553                 //otherwise swap the aligned el to the opposite border of the target.
9554                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9555                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9556                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9557                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9558
9559                var doc = document;
9560                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9561                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9562
9563                if((x+w) > dw + scrollX){
9564                     x = swapX ? r.left-w : dw+scrollX-w;
9565                 }
9566                if(x < scrollX){
9567                    x = swapX ? r.right : scrollX;
9568                }
9569                if((y+h) > dh + scrollY){
9570                     y = swapY ? r.top-h : dh+scrollY-h;
9571                 }
9572                if (y < scrollY){
9573                    y = swapY ? r.bottom : scrollY;
9574                }
9575             }
9576             return [x,y];
9577         },
9578
9579         // private
9580         getConstrainToXY : function(){
9581             var os = {top:0, left:0, bottom:0, right: 0};
9582
9583             return function(el, local, offsets, proposedXY){
9584                 el = Roo.get(el);
9585                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9586
9587                 var vw, vh, vx = 0, vy = 0;
9588                 if(el.dom == document.body || el.dom == document){
9589                     vw = Roo.lib.Dom.getViewWidth();
9590                     vh = Roo.lib.Dom.getViewHeight();
9591                 }else{
9592                     vw = el.dom.clientWidth;
9593                     vh = el.dom.clientHeight;
9594                     if(!local){
9595                         var vxy = el.getXY();
9596                         vx = vxy[0];
9597                         vy = vxy[1];
9598                     }
9599                 }
9600
9601                 var s = el.getScroll();
9602
9603                 vx += offsets.left + s.left;
9604                 vy += offsets.top + s.top;
9605
9606                 vw -= offsets.right;
9607                 vh -= offsets.bottom;
9608
9609                 var vr = vx+vw;
9610                 var vb = vy+vh;
9611
9612                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9613                 var x = xy[0], y = xy[1];
9614                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9615
9616                 // only move it if it needs it
9617                 var moved = false;
9618
9619                 // first validate right/bottom
9620                 if((x + w) > vr){
9621                     x = vr - w;
9622                     moved = true;
9623                 }
9624                 if((y + h) > vb){
9625                     y = vb - h;
9626                     moved = true;
9627                 }
9628                 // then make sure top/left isn't negative
9629                 if(x < vx){
9630                     x = vx;
9631                     moved = true;
9632                 }
9633                 if(y < vy){
9634                     y = vy;
9635                     moved = true;
9636                 }
9637                 return moved ? [x, y] : false;
9638             };
9639         }(),
9640
9641         // private
9642         adjustForConstraints : function(xy, parent, offsets){
9643             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9644         },
9645
9646         /**
9647          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9648          * document it aligns it to the viewport.
9649          * The position parameter is optional, and can be specified in any one of the following formats:
9650          * <ul>
9651          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9652          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9653          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9654          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9655          *   <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
9656          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9657          * </ul>
9658          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9659          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9660          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9661          * that specified in order to enforce the viewport constraints.
9662          * Following are all of the supported anchor positions:
9663     <pre>
9664     Value  Description
9665     -----  -----------------------------
9666     tl     The top left corner (default)
9667     t      The center of the top edge
9668     tr     The top right corner
9669     l      The center of the left edge
9670     c      In the center of the element
9671     r      The center of the right edge
9672     bl     The bottom left corner
9673     b      The center of the bottom edge
9674     br     The bottom right corner
9675     </pre>
9676     Example Usage:
9677     <pre><code>
9678     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9679     el.alignTo("other-el");
9680
9681     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9682     el.alignTo("other-el", "tr?");
9683
9684     // align the bottom right corner of el with the center left edge of other-el
9685     el.alignTo("other-el", "br-l?");
9686
9687     // align the center of el with the bottom left corner of other-el and
9688     // adjust the x position by -6 pixels (and the y position by 0)
9689     el.alignTo("other-el", "c-bl", [-6, 0]);
9690     </code></pre>
9691          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9692          * @param {String} position The position to align to.
9693          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9694          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9695          * @return {Roo.Element} this
9696          */
9697         alignTo : function(element, position, offsets, animate){
9698             var xy = this.getAlignToXY(element, position, offsets);
9699             this.setXY(xy, this.preanim(arguments, 3));
9700             return this;
9701         },
9702
9703         /**
9704          * Anchors an element to another element and realigns it when the window is resized.
9705          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9706          * @param {String} position The position to align to.
9707          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9708          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9709          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9710          * is a number, it is used as the buffer delay (defaults to 50ms).
9711          * @param {Function} callback The function to call after the animation finishes
9712          * @return {Roo.Element} this
9713          */
9714         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9715             var action = function(){
9716                 this.alignTo(el, alignment, offsets, animate);
9717                 Roo.callback(callback, this);
9718             };
9719             Roo.EventManager.onWindowResize(action, this);
9720             var tm = typeof monitorScroll;
9721             if(tm != 'undefined'){
9722                 Roo.EventManager.on(window, 'scroll', action, this,
9723                     {buffer: tm == 'number' ? monitorScroll : 50});
9724             }
9725             action.call(this); // align immediately
9726             return this;
9727         },
9728         /**
9729          * Clears any opacity settings from this element. Required in some cases for IE.
9730          * @return {Roo.Element} this
9731          */
9732         clearOpacity : function(){
9733             if (window.ActiveXObject) {
9734                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9735                     this.dom.style.filter = "";
9736                 }
9737             } else {
9738                 this.dom.style.opacity = "";
9739                 this.dom.style["-moz-opacity"] = "";
9740                 this.dom.style["-khtml-opacity"] = "";
9741             }
9742             return this;
9743         },
9744
9745         /**
9746          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9747          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9748          * @return {Roo.Element} this
9749          */
9750         hide : function(animate){
9751             this.setVisible(false, this.preanim(arguments, 0));
9752             return this;
9753         },
9754
9755         /**
9756         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9757         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9758          * @return {Roo.Element} this
9759          */
9760         show : function(animate){
9761             this.setVisible(true, this.preanim(arguments, 0));
9762             return this;
9763         },
9764
9765         /**
9766          * @private Test if size has a unit, otherwise appends the default
9767          */
9768         addUnits : function(size){
9769             return Roo.Element.addUnits(size, this.defaultUnit);
9770         },
9771
9772         /**
9773          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9774          * @return {Roo.Element} this
9775          */
9776         beginMeasure : function(){
9777             var el = this.dom;
9778             if(el.offsetWidth || el.offsetHeight){
9779                 return this; // offsets work already
9780             }
9781             var changed = [];
9782             var p = this.dom, b = document.body; // start with this element
9783             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9784                 var pe = Roo.get(p);
9785                 if(pe.getStyle('display') == 'none'){
9786                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9787                     p.style.visibility = "hidden";
9788                     p.style.display = "block";
9789                 }
9790                 p = p.parentNode;
9791             }
9792             this._measureChanged = changed;
9793             return this;
9794
9795         },
9796
9797         /**
9798          * Restores displays to before beginMeasure was called
9799          * @return {Roo.Element} this
9800          */
9801         endMeasure : function(){
9802             var changed = this._measureChanged;
9803             if(changed){
9804                 for(var i = 0, len = changed.length; i < len; i++) {
9805                     var r = changed[i];
9806                     r.el.style.visibility = r.visibility;
9807                     r.el.style.display = "none";
9808                 }
9809                 this._measureChanged = null;
9810             }
9811             return this;
9812         },
9813
9814         /**
9815         * Update the innerHTML of this element, optionally searching for and processing scripts
9816         * @param {String} html The new HTML
9817         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9818         * @param {Function} callback For async script loading you can be noticed when the update completes
9819         * @return {Roo.Element} this
9820          */
9821         update : function(html, loadScripts, callback){
9822             if(typeof html == "undefined"){
9823                 html = "";
9824             }
9825             if(loadScripts !== true){
9826                 this.dom.innerHTML = html;
9827                 if(typeof callback == "function"){
9828                     callback();
9829                 }
9830                 return this;
9831             }
9832             var id = Roo.id();
9833             var dom = this.dom;
9834
9835             html += '<span id="' + id + '"></span>';
9836
9837             E.onAvailable(id, function(){
9838                 var hd = document.getElementsByTagName("head")[0];
9839                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9840                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9841                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9842
9843                 var match;
9844                 while(match = re.exec(html)){
9845                     var attrs = match[1];
9846                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9847                     if(srcMatch && srcMatch[2]){
9848                        var s = document.createElement("script");
9849                        s.src = srcMatch[2];
9850                        var typeMatch = attrs.match(typeRe);
9851                        if(typeMatch && typeMatch[2]){
9852                            s.type = typeMatch[2];
9853                        }
9854                        hd.appendChild(s);
9855                     }else if(match[2] && match[2].length > 0){
9856                         if(window.execScript) {
9857                            window.execScript(match[2]);
9858                         } else {
9859                             /**
9860                              * eval:var:id
9861                              * eval:var:dom
9862                              * eval:var:html
9863                              * 
9864                              */
9865                            window.eval(match[2]);
9866                         }
9867                     }
9868                 }
9869                 var el = document.getElementById(id);
9870                 if(el){el.parentNode.removeChild(el);}
9871                 if(typeof callback == "function"){
9872                     callback();
9873                 }
9874             });
9875             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9876             return this;
9877         },
9878
9879         /**
9880          * Direct access to the UpdateManager update() method (takes the same parameters).
9881          * @param {String/Function} url The url for this request or a function to call to get the url
9882          * @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}
9883          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9884          * @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.
9885          * @return {Roo.Element} this
9886          */
9887         load : function(){
9888             var um = this.getUpdateManager();
9889             um.update.apply(um, arguments);
9890             return this;
9891         },
9892
9893         /**
9894         * Gets this element's UpdateManager
9895         * @return {Roo.UpdateManager} The UpdateManager
9896         */
9897         getUpdateManager : function(){
9898             if(!this.updateManager){
9899                 this.updateManager = new Roo.UpdateManager(this);
9900             }
9901             return this.updateManager;
9902         },
9903
9904         /**
9905          * Disables text selection for this element (normalized across browsers)
9906          * @return {Roo.Element} this
9907          */
9908         unselectable : function(){
9909             this.dom.unselectable = "on";
9910             this.swallowEvent("selectstart", true);
9911             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9912             this.addClass("x-unselectable");
9913             return this;
9914         },
9915
9916         /**
9917         * Calculates the x, y to center this element on the screen
9918         * @return {Array} The x, y values [x, y]
9919         */
9920         getCenterXY : function(){
9921             return this.getAlignToXY(document, 'c-c');
9922         },
9923
9924         /**
9925         * Centers the Element in either the viewport, or another Element.
9926         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9927         */
9928         center : function(centerIn){
9929             this.alignTo(centerIn || document, 'c-c');
9930             return this;
9931         },
9932
9933         /**
9934          * Tests various css rules/browsers to determine if this element uses a border box
9935          * @return {Boolean}
9936          */
9937         isBorderBox : function(){
9938             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9939         },
9940
9941         /**
9942          * Return a box {x, y, width, height} that can be used to set another elements
9943          * size/location to match this element.
9944          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9945          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9946          * @return {Object} box An object in the format {x, y, width, height}
9947          */
9948         getBox : function(contentBox, local){
9949             var xy;
9950             if(!local){
9951                 xy = this.getXY();
9952             }else{
9953                 var left = parseInt(this.getStyle("left"), 10) || 0;
9954                 var top = parseInt(this.getStyle("top"), 10) || 0;
9955                 xy = [left, top];
9956             }
9957             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9958             if(!contentBox){
9959                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9960             }else{
9961                 var l = this.getBorderWidth("l")+this.getPadding("l");
9962                 var r = this.getBorderWidth("r")+this.getPadding("r");
9963                 var t = this.getBorderWidth("t")+this.getPadding("t");
9964                 var b = this.getBorderWidth("b")+this.getPadding("b");
9965                 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)};
9966             }
9967             bx.right = bx.x + bx.width;
9968             bx.bottom = bx.y + bx.height;
9969             return bx;
9970         },
9971
9972         /**
9973          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9974          for more information about the sides.
9975          * @param {String} sides
9976          * @return {Number}
9977          */
9978         getFrameWidth : function(sides, onlyContentBox){
9979             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9980         },
9981
9982         /**
9983          * 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.
9984          * @param {Object} box The box to fill {x, y, width, height}
9985          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9986          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9987          * @return {Roo.Element} this
9988          */
9989         setBox : function(box, adjust, animate){
9990             var w = box.width, h = box.height;
9991             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9992                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9993                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9994             }
9995             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9996             return this;
9997         },
9998
9999         /**
10000          * Forces the browser to repaint this element
10001          * @return {Roo.Element} this
10002          */
10003          repaint : function(){
10004             var dom = this.dom;
10005             this.addClass("x-repaint");
10006             setTimeout(function(){
10007                 Roo.get(dom).removeClass("x-repaint");
10008             }, 1);
10009             return this;
10010         },
10011
10012         /**
10013          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10014          * then it returns the calculated width of the sides (see getPadding)
10015          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10016          * @return {Object/Number}
10017          */
10018         getMargins : function(side){
10019             if(!side){
10020                 return {
10021                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10022                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10023                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10024                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10025                 };
10026             }else{
10027                 return this.addStyles(side, El.margins);
10028              }
10029         },
10030
10031         // private
10032         addStyles : function(sides, styles){
10033             var val = 0, v, w;
10034             for(var i = 0, len = sides.length; i < len; i++){
10035                 v = this.getStyle(styles[sides.charAt(i)]);
10036                 if(v){
10037                      w = parseInt(v, 10);
10038                      if(w){ val += w; }
10039                 }
10040             }
10041             return val;
10042         },
10043
10044         /**
10045          * Creates a proxy element of this element
10046          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10047          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10048          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10049          * @return {Roo.Element} The new proxy element
10050          */
10051         createProxy : function(config, renderTo, matchBox){
10052             if(renderTo){
10053                 renderTo = Roo.getDom(renderTo);
10054             }else{
10055                 renderTo = document.body;
10056             }
10057             config = typeof config == "object" ?
10058                 config : {tag : "div", cls: config};
10059             var proxy = Roo.DomHelper.append(renderTo, config, true);
10060             if(matchBox){
10061                proxy.setBox(this.getBox());
10062             }
10063             return proxy;
10064         },
10065
10066         /**
10067          * Puts a mask over this element to disable user interaction. Requires core.css.
10068          * This method can only be applied to elements which accept child nodes.
10069          * @param {String} msg (optional) A message to display in the mask
10070          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10071          * @return {Element} The mask  element
10072          */
10073         mask : function(msg, msgCls)
10074         {
10075             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10076                 this.setStyle("position", "relative");
10077             }
10078             if(!this._mask){
10079                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10080             }
10081             
10082             this.addClass("x-masked");
10083             this._mask.setDisplayed(true);
10084             
10085             // we wander
10086             var z = 0;
10087             var dom = this.dom;
10088             while (dom && dom.style) {
10089                 if (!isNaN(parseInt(dom.style.zIndex))) {
10090                     z = Math.max(z, parseInt(dom.style.zIndex));
10091                 }
10092                 dom = dom.parentNode;
10093             }
10094             // if we are masking the body - then it hides everything..
10095             if (this.dom == document.body) {
10096                 z = 1000000;
10097                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10098                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10099             }
10100            
10101             if(typeof msg == 'string'){
10102                 if(!this._maskMsg){
10103                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10104                         cls: "roo-el-mask-msg", 
10105                         cn: [
10106                             {
10107                                 tag: 'i',
10108                                 cls: 'fa fa-spinner fa-spin'
10109                             },
10110                             {
10111                                 tag: 'div'
10112                             }   
10113                         ]
10114                     }, true);
10115                 }
10116                 var mm = this._maskMsg;
10117                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10118                 if (mm.dom.lastChild) { // weird IE issue?
10119                     mm.dom.lastChild.innerHTML = msg;
10120                 }
10121                 mm.setDisplayed(true);
10122                 mm.center(this);
10123                 mm.setStyle('z-index', z + 102);
10124             }
10125             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10126                 this._mask.setHeight(this.getHeight());
10127             }
10128             this._mask.setStyle('z-index', z + 100);
10129             
10130             return this._mask;
10131         },
10132
10133         /**
10134          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10135          * it is cached for reuse.
10136          */
10137         unmask : function(removeEl){
10138             if(this._mask){
10139                 if(removeEl === true){
10140                     this._mask.remove();
10141                     delete this._mask;
10142                     if(this._maskMsg){
10143                         this._maskMsg.remove();
10144                         delete this._maskMsg;
10145                     }
10146                 }else{
10147                     this._mask.setDisplayed(false);
10148                     if(this._maskMsg){
10149                         this._maskMsg.setDisplayed(false);
10150                     }
10151                 }
10152             }
10153             this.removeClass("x-masked");
10154         },
10155
10156         /**
10157          * Returns true if this element is masked
10158          * @return {Boolean}
10159          */
10160         isMasked : function(){
10161             return this._mask && this._mask.isVisible();
10162         },
10163
10164         /**
10165          * Creates an iframe shim for this element to keep selects and other windowed objects from
10166          * showing through.
10167          * @return {Roo.Element} The new shim element
10168          */
10169         createShim : function(){
10170             var el = document.createElement('iframe');
10171             el.frameBorder = 'no';
10172             el.className = 'roo-shim';
10173             if(Roo.isIE && Roo.isSecure){
10174                 el.src = Roo.SSL_SECURE_URL;
10175             }
10176             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10177             shim.autoBoxAdjust = false;
10178             return shim;
10179         },
10180
10181         /**
10182          * Removes this element from the DOM and deletes it from the cache
10183          */
10184         remove : function(){
10185             if(this.dom.parentNode){
10186                 this.dom.parentNode.removeChild(this.dom);
10187             }
10188             delete El.cache[this.dom.id];
10189         },
10190
10191         /**
10192          * Sets up event handlers to add and remove a css class when the mouse is over this element
10193          * @param {String} className
10194          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10195          * mouseout events for children elements
10196          * @return {Roo.Element} this
10197          */
10198         addClassOnOver : function(className, preventFlicker){
10199             this.on("mouseover", function(){
10200                 Roo.fly(this, '_internal').addClass(className);
10201             }, this.dom);
10202             var removeFn = function(e){
10203                 if(preventFlicker !== true || !e.within(this, true)){
10204                     Roo.fly(this, '_internal').removeClass(className);
10205                 }
10206             };
10207             this.on("mouseout", removeFn, this.dom);
10208             return this;
10209         },
10210
10211         /**
10212          * Sets up event handlers to add and remove a css class when this element has the focus
10213          * @param {String} className
10214          * @return {Roo.Element} this
10215          */
10216         addClassOnFocus : function(className){
10217             this.on("focus", function(){
10218                 Roo.fly(this, '_internal').addClass(className);
10219             }, this.dom);
10220             this.on("blur", function(){
10221                 Roo.fly(this, '_internal').removeClass(className);
10222             }, this.dom);
10223             return this;
10224         },
10225         /**
10226          * 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)
10227          * @param {String} className
10228          * @return {Roo.Element} this
10229          */
10230         addClassOnClick : function(className){
10231             var dom = this.dom;
10232             this.on("mousedown", function(){
10233                 Roo.fly(dom, '_internal').addClass(className);
10234                 var d = Roo.get(document);
10235                 var fn = function(){
10236                     Roo.fly(dom, '_internal').removeClass(className);
10237                     d.removeListener("mouseup", fn);
10238                 };
10239                 d.on("mouseup", fn);
10240             });
10241             return this;
10242         },
10243
10244         /**
10245          * Stops the specified event from bubbling and optionally prevents the default action
10246          * @param {String} eventName
10247          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10248          * @return {Roo.Element} this
10249          */
10250         swallowEvent : function(eventName, preventDefault){
10251             var fn = function(e){
10252                 e.stopPropagation();
10253                 if(preventDefault){
10254                     e.preventDefault();
10255                 }
10256             };
10257             if(eventName instanceof Array){
10258                 for(var i = 0, len = eventName.length; i < len; i++){
10259                      this.on(eventName[i], fn);
10260                 }
10261                 return this;
10262             }
10263             this.on(eventName, fn);
10264             return this;
10265         },
10266
10267         /**
10268          * @private
10269          */
10270         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10271
10272         /**
10273          * Sizes this element to its parent element's dimensions performing
10274          * neccessary box adjustments.
10275          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10276          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10277          * @return {Roo.Element} this
10278          */
10279         fitToParent : function(monitorResize, targetParent) {
10280           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10281           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10282           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10283             return this;
10284           }
10285           var p = Roo.get(targetParent || this.dom.parentNode);
10286           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10287           if (monitorResize === true) {
10288             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10289             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10290           }
10291           return this;
10292         },
10293
10294         /**
10295          * Gets the next sibling, skipping text nodes
10296          * @return {HTMLElement} The next sibling or null
10297          */
10298         getNextSibling : function(){
10299             var n = this.dom.nextSibling;
10300             while(n && n.nodeType != 1){
10301                 n = n.nextSibling;
10302             }
10303             return n;
10304         },
10305
10306         /**
10307          * Gets the previous sibling, skipping text nodes
10308          * @return {HTMLElement} The previous sibling or null
10309          */
10310         getPrevSibling : function(){
10311             var n = this.dom.previousSibling;
10312             while(n && n.nodeType != 1){
10313                 n = n.previousSibling;
10314             }
10315             return n;
10316         },
10317
10318
10319         /**
10320          * Appends the passed element(s) to this element
10321          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10322          * @return {Roo.Element} this
10323          */
10324         appendChild: function(el){
10325             el = Roo.get(el);
10326             el.appendTo(this);
10327             return this;
10328         },
10329
10330         /**
10331          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10332          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10333          * automatically generated with the specified attributes.
10334          * @param {HTMLElement} insertBefore (optional) a child element of this element
10335          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10336          * @return {Roo.Element} The new child element
10337          */
10338         createChild: function(config, insertBefore, returnDom){
10339             config = config || {tag:'div'};
10340             if(insertBefore){
10341                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10342             }
10343             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10344         },
10345
10346         /**
10347          * Appends this element to the passed element
10348          * @param {String/HTMLElement/Element} el The new parent element
10349          * @return {Roo.Element} this
10350          */
10351         appendTo: function(el){
10352             el = Roo.getDom(el);
10353             el.appendChild(this.dom);
10354             return this;
10355         },
10356
10357         /**
10358          * Inserts this element before the passed element in the DOM
10359          * @param {String/HTMLElement/Element} el The element to insert before
10360          * @return {Roo.Element} this
10361          */
10362         insertBefore: function(el){
10363             el = Roo.getDom(el);
10364             el.parentNode.insertBefore(this.dom, el);
10365             return this;
10366         },
10367
10368         /**
10369          * Inserts this element after the passed element in the DOM
10370          * @param {String/HTMLElement/Element} el The element to insert after
10371          * @return {Roo.Element} this
10372          */
10373         insertAfter: function(el){
10374             el = Roo.getDom(el);
10375             el.parentNode.insertBefore(this.dom, el.nextSibling);
10376             return this;
10377         },
10378
10379         /**
10380          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10381          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10382          * @return {Roo.Element} The new child
10383          */
10384         insertFirst: function(el, returnDom){
10385             el = el || {};
10386             if(typeof el == 'object' && !el.nodeType){ // dh config
10387                 return this.createChild(el, this.dom.firstChild, returnDom);
10388             }else{
10389                 el = Roo.getDom(el);
10390                 this.dom.insertBefore(el, this.dom.firstChild);
10391                 return !returnDom ? Roo.get(el) : el;
10392             }
10393         },
10394
10395         /**
10396          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10397          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10398          * @param {String} where (optional) 'before' or 'after' defaults to before
10399          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10400          * @return {Roo.Element} the inserted Element
10401          */
10402         insertSibling: function(el, where, returnDom){
10403             where = where ? where.toLowerCase() : 'before';
10404             el = el || {};
10405             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10406
10407             if(typeof el == 'object' && !el.nodeType){ // dh config
10408                 if(where == 'after' && !this.dom.nextSibling){
10409                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10410                 }else{
10411                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10412                 }
10413
10414             }else{
10415                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10416                             where == 'before' ? this.dom : this.dom.nextSibling);
10417                 if(!returnDom){
10418                     rt = Roo.get(rt);
10419                 }
10420             }
10421             return rt;
10422         },
10423
10424         /**
10425          * Creates and wraps this element with another element
10426          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10427          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10428          * @return {HTMLElement/Element} The newly created wrapper element
10429          */
10430         wrap: function(config, returnDom){
10431             if(!config){
10432                 config = {tag: "div"};
10433             }
10434             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10435             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10436             return newEl;
10437         },
10438
10439         /**
10440          * Replaces the passed element with this element
10441          * @param {String/HTMLElement/Element} el The element to replace
10442          * @return {Roo.Element} this
10443          */
10444         replace: function(el){
10445             el = Roo.get(el);
10446             this.insertBefore(el);
10447             el.remove();
10448             return this;
10449         },
10450
10451         /**
10452          * Inserts an html fragment into this element
10453          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10454          * @param {String} html The HTML fragment
10455          * @param {Boolean} returnEl True to return an Roo.Element
10456          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10457          */
10458         insertHtml : function(where, html, returnEl){
10459             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10460             return returnEl ? Roo.get(el) : el;
10461         },
10462
10463         /**
10464          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10465          * @param {Object} o The object with the attributes
10466          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10467          * @return {Roo.Element} this
10468          */
10469         set : function(o, useSet){
10470             var el = this.dom;
10471             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10472             for(var attr in o){
10473                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10474                 if(attr=="cls"){
10475                     el.className = o["cls"];
10476                 }else{
10477                     if(useSet) {
10478                         el.setAttribute(attr, o[attr]);
10479                     } else {
10480                         el[attr] = o[attr];
10481                     }
10482                 }
10483             }
10484             if(o.style){
10485                 Roo.DomHelper.applyStyles(el, o.style);
10486             }
10487             return this;
10488         },
10489
10490         /**
10491          * Convenience method for constructing a KeyMap
10492          * @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:
10493          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10494          * @param {Function} fn The function to call
10495          * @param {Object} scope (optional) The scope of the function
10496          * @return {Roo.KeyMap} The KeyMap created
10497          */
10498         addKeyListener : function(key, fn, scope){
10499             var config;
10500             if(typeof key != "object" || key instanceof Array){
10501                 config = {
10502                     key: key,
10503                     fn: fn,
10504                     scope: scope
10505                 };
10506             }else{
10507                 config = {
10508                     key : key.key,
10509                     shift : key.shift,
10510                     ctrl : key.ctrl,
10511                     alt : key.alt,
10512                     fn: fn,
10513                     scope: scope
10514                 };
10515             }
10516             return new Roo.KeyMap(this, config);
10517         },
10518
10519         /**
10520          * Creates a KeyMap for this element
10521          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10522          * @return {Roo.KeyMap} The KeyMap created
10523          */
10524         addKeyMap : function(config){
10525             return new Roo.KeyMap(this, config);
10526         },
10527
10528         /**
10529          * Returns true if this element is scrollable.
10530          * @return {Boolean}
10531          */
10532          isScrollable : function(){
10533             var dom = this.dom;
10534             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10535         },
10536
10537         /**
10538          * 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().
10539          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10540          * @param {Number} value The new scroll value
10541          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10542          * @return {Element} this
10543          */
10544
10545         scrollTo : function(side, value, animate){
10546             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10547             if(!animate || !A){
10548                 this.dom[prop] = value;
10549             }else{
10550                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10551                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10552             }
10553             return this;
10554         },
10555
10556         /**
10557          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10558          * within this element's scrollable range.
10559          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10560          * @param {Number} distance How far to scroll the element in pixels
10561          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10562          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10563          * was scrolled as far as it could go.
10564          */
10565          scroll : function(direction, distance, animate){
10566              if(!this.isScrollable()){
10567                  return;
10568              }
10569              var el = this.dom;
10570              var l = el.scrollLeft, t = el.scrollTop;
10571              var w = el.scrollWidth, h = el.scrollHeight;
10572              var cw = el.clientWidth, ch = el.clientHeight;
10573              direction = direction.toLowerCase();
10574              var scrolled = false;
10575              var a = this.preanim(arguments, 2);
10576              switch(direction){
10577                  case "l":
10578                  case "left":
10579                      if(w - l > cw){
10580                          var v = Math.min(l + distance, w-cw);
10581                          this.scrollTo("left", v, a);
10582                          scrolled = true;
10583                      }
10584                      break;
10585                 case "r":
10586                 case "right":
10587                      if(l > 0){
10588                          var v = Math.max(l - distance, 0);
10589                          this.scrollTo("left", v, a);
10590                          scrolled = true;
10591                      }
10592                      break;
10593                 case "t":
10594                 case "top":
10595                 case "up":
10596                      if(t > 0){
10597                          var v = Math.max(t - distance, 0);
10598                          this.scrollTo("top", v, a);
10599                          scrolled = true;
10600                      }
10601                      break;
10602                 case "b":
10603                 case "bottom":
10604                 case "down":
10605                      if(h - t > ch){
10606                          var v = Math.min(t + distance, h-ch);
10607                          this.scrollTo("top", v, a);
10608                          scrolled = true;
10609                      }
10610                      break;
10611              }
10612              return scrolled;
10613         },
10614
10615         /**
10616          * Translates the passed page coordinates into left/top css values for this element
10617          * @param {Number/Array} x The page x or an array containing [x, y]
10618          * @param {Number} y The page y
10619          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10620          */
10621         translatePoints : function(x, y){
10622             if(typeof x == 'object' || x instanceof Array){
10623                 y = x[1]; x = x[0];
10624             }
10625             var p = this.getStyle('position');
10626             var o = this.getXY();
10627
10628             var l = parseInt(this.getStyle('left'), 10);
10629             var t = parseInt(this.getStyle('top'), 10);
10630
10631             if(isNaN(l)){
10632                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10633             }
10634             if(isNaN(t)){
10635                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10636             }
10637
10638             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10639         },
10640
10641         /**
10642          * Returns the current scroll position of the element.
10643          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10644          */
10645         getScroll : function(){
10646             var d = this.dom, doc = document;
10647             if(d == doc || d == doc.body){
10648                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10649                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10650                 return {left: l, top: t};
10651             }else{
10652                 return {left: d.scrollLeft, top: d.scrollTop};
10653             }
10654         },
10655
10656         /**
10657          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10658          * are convert to standard 6 digit hex color.
10659          * @param {String} attr The css attribute
10660          * @param {String} defaultValue The default value to use when a valid color isn't found
10661          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10662          * YUI color anims.
10663          */
10664         getColor : function(attr, defaultValue, prefix){
10665             var v = this.getStyle(attr);
10666             if(!v || v == "transparent" || v == "inherit") {
10667                 return defaultValue;
10668             }
10669             var color = typeof prefix == "undefined" ? "#" : prefix;
10670             if(v.substr(0, 4) == "rgb("){
10671                 var rvs = v.slice(4, v.length -1).split(",");
10672                 for(var i = 0; i < 3; i++){
10673                     var h = parseInt(rvs[i]).toString(16);
10674                     if(h < 16){
10675                         h = "0" + h;
10676                     }
10677                     color += h;
10678                 }
10679             } else {
10680                 if(v.substr(0, 1) == "#"){
10681                     if(v.length == 4) {
10682                         for(var i = 1; i < 4; i++){
10683                             var c = v.charAt(i);
10684                             color +=  c + c;
10685                         }
10686                     }else if(v.length == 7){
10687                         color += v.substr(1);
10688                     }
10689                 }
10690             }
10691             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10692         },
10693
10694         /**
10695          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10696          * gradient background, rounded corners and a 4-way shadow.
10697          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10698          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10699          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10700          * @return {Roo.Element} this
10701          */
10702         boxWrap : function(cls){
10703             cls = cls || 'x-box';
10704             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10705             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10706             return el;
10707         },
10708
10709         /**
10710          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10711          * @param {String} namespace The namespace in which to look for the attribute
10712          * @param {String} name The attribute name
10713          * @return {String} The attribute value
10714          */
10715         getAttributeNS : Roo.isIE ? function(ns, name){
10716             var d = this.dom;
10717             var type = typeof d[ns+":"+name];
10718             if(type != 'undefined' && type != 'unknown'){
10719                 return d[ns+":"+name];
10720             }
10721             return d[name];
10722         } : function(ns, name){
10723             var d = this.dom;
10724             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10725         },
10726         
10727         
10728         /**
10729          * Sets or Returns the value the dom attribute value
10730          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10731          * @param {String} value (optional) The value to set the attribute to
10732          * @return {String} The attribute value
10733          */
10734         attr : function(name){
10735             if (arguments.length > 1) {
10736                 this.dom.setAttribute(name, arguments[1]);
10737                 return arguments[1];
10738             }
10739             if (typeof(name) == 'object') {
10740                 for(var i in name) {
10741                     this.attr(i, name[i]);
10742                 }
10743                 return name;
10744             }
10745             
10746             
10747             if (!this.dom.hasAttribute(name)) {
10748                 return undefined;
10749             }
10750             return this.dom.getAttribute(name);
10751         }
10752         
10753         
10754         
10755     };
10756
10757     var ep = El.prototype;
10758
10759     /**
10760      * Appends an event handler (Shorthand for addListener)
10761      * @param {String}   eventName     The type of event to append
10762      * @param {Function} fn        The method the event invokes
10763      * @param {Object} scope       (optional) The scope (this object) of the fn
10764      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10765      * @method
10766      */
10767     ep.on = ep.addListener;
10768         // backwards compat
10769     ep.mon = ep.addListener;
10770
10771     /**
10772      * Removes an event handler from this element (shorthand for removeListener)
10773      * @param {String} eventName the type of event to remove
10774      * @param {Function} fn the method the event invokes
10775      * @return {Roo.Element} this
10776      * @method
10777      */
10778     ep.un = ep.removeListener;
10779
10780     /**
10781      * true to automatically adjust width and height settings for box-model issues (default to true)
10782      */
10783     ep.autoBoxAdjust = true;
10784
10785     // private
10786     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10787
10788     // private
10789     El.addUnits = function(v, defaultUnit){
10790         if(v === "" || v == "auto"){
10791             return v;
10792         }
10793         if(v === undefined){
10794             return '';
10795         }
10796         if(typeof v == "number" || !El.unitPattern.test(v)){
10797             return v + (defaultUnit || 'px');
10798         }
10799         return v;
10800     };
10801
10802     // special markup used throughout Roo when box wrapping elements
10803     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>';
10804     /**
10805      * Visibility mode constant - Use visibility to hide element
10806      * @static
10807      * @type Number
10808      */
10809     El.VISIBILITY = 1;
10810     /**
10811      * Visibility mode constant - Use display to hide element
10812      * @static
10813      * @type Number
10814      */
10815     El.DISPLAY = 2;
10816
10817     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10818     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10819     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10820
10821
10822
10823     /**
10824      * @private
10825      */
10826     El.cache = {};
10827
10828     var docEl;
10829
10830     /**
10831      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10832      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10833      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10834      * @return {Element} The Element object
10835      * @static
10836      */
10837     El.get = function(el){
10838         var ex, elm, id;
10839         if(!el){ return null; }
10840         if(typeof el == "string"){ // element id
10841             if(!(elm = document.getElementById(el))){
10842                 return null;
10843             }
10844             if(ex = El.cache[el]){
10845                 ex.dom = elm;
10846             }else{
10847                 ex = El.cache[el] = new El(elm);
10848             }
10849             return ex;
10850         }else if(el.tagName){ // dom element
10851             if(!(id = el.id)){
10852                 id = Roo.id(el);
10853             }
10854             if(ex = El.cache[id]){
10855                 ex.dom = el;
10856             }else{
10857                 ex = El.cache[id] = new El(el);
10858             }
10859             return ex;
10860         }else if(el instanceof El){
10861             if(el != docEl){
10862                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10863                                                               // catch case where it hasn't been appended
10864                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10865             }
10866             return el;
10867         }else if(el.isComposite){
10868             return el;
10869         }else if(el instanceof Array){
10870             return El.select(el);
10871         }else if(el == document){
10872             // create a bogus element object representing the document object
10873             if(!docEl){
10874                 var f = function(){};
10875                 f.prototype = El.prototype;
10876                 docEl = new f();
10877                 docEl.dom = document;
10878             }
10879             return docEl;
10880         }
10881         return null;
10882     };
10883
10884     // private
10885     El.uncache = function(el){
10886         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10887             if(a[i]){
10888                 delete El.cache[a[i].id || a[i]];
10889             }
10890         }
10891     };
10892
10893     // private
10894     // Garbage collection - uncache elements/purge listeners on orphaned elements
10895     // so we don't hold a reference and cause the browser to retain them
10896     El.garbageCollect = function(){
10897         if(!Roo.enableGarbageCollector){
10898             clearInterval(El.collectorThread);
10899             return;
10900         }
10901         for(var eid in El.cache){
10902             var el = El.cache[eid], d = el.dom;
10903             // -------------------------------------------------------
10904             // Determining what is garbage:
10905             // -------------------------------------------------------
10906             // !d
10907             // dom node is null, definitely garbage
10908             // -------------------------------------------------------
10909             // !d.parentNode
10910             // no parentNode == direct orphan, definitely garbage
10911             // -------------------------------------------------------
10912             // !d.offsetParent && !document.getElementById(eid)
10913             // display none elements have no offsetParent so we will
10914             // also try to look it up by it's id. However, check
10915             // offsetParent first so we don't do unneeded lookups.
10916             // This enables collection of elements that are not orphans
10917             // directly, but somewhere up the line they have an orphan
10918             // parent.
10919             // -------------------------------------------------------
10920             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10921                 delete El.cache[eid];
10922                 if(d && Roo.enableListenerCollection){
10923                     E.purgeElement(d);
10924                 }
10925             }
10926         }
10927     }
10928     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10929
10930
10931     // dom is optional
10932     El.Flyweight = function(dom){
10933         this.dom = dom;
10934     };
10935     El.Flyweight.prototype = El.prototype;
10936
10937     El._flyweights = {};
10938     /**
10939      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10940      * the dom node can be overwritten by other code.
10941      * @param {String/HTMLElement} el The dom node or id
10942      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10943      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10944      * @static
10945      * @return {Element} The shared Element object
10946      */
10947     El.fly = function(el, named){
10948         named = named || '_global';
10949         el = Roo.getDom(el);
10950         if(!el){
10951             return null;
10952         }
10953         if(!El._flyweights[named]){
10954             El._flyweights[named] = new El.Flyweight();
10955         }
10956         El._flyweights[named].dom = el;
10957         return El._flyweights[named];
10958     };
10959
10960     /**
10961      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10962      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10963      * Shorthand of {@link Roo.Element#get}
10964      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10965      * @return {Element} The Element object
10966      * @member Roo
10967      * @method get
10968      */
10969     Roo.get = El.get;
10970     /**
10971      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10972      * the dom node can be overwritten by other code.
10973      * Shorthand of {@link Roo.Element#fly}
10974      * @param {String/HTMLElement} el The dom node or id
10975      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10976      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10977      * @static
10978      * @return {Element} The shared Element object
10979      * @member Roo
10980      * @method fly
10981      */
10982     Roo.fly = El.fly;
10983
10984     // speedy lookup for elements never to box adjust
10985     var noBoxAdjust = Roo.isStrict ? {
10986         select:1
10987     } : {
10988         input:1, select:1, textarea:1
10989     };
10990     if(Roo.isIE || Roo.isGecko){
10991         noBoxAdjust['button'] = 1;
10992     }
10993
10994
10995     Roo.EventManager.on(window, 'unload', function(){
10996         delete El.cache;
10997         delete El._flyweights;
10998     });
10999 })();
11000
11001
11002
11003
11004 if(Roo.DomQuery){
11005     Roo.Element.selectorFunction = Roo.DomQuery.select;
11006 }
11007
11008 Roo.Element.select = function(selector, unique, root){
11009     var els;
11010     if(typeof selector == "string"){
11011         els = Roo.Element.selectorFunction(selector, root);
11012     }else if(selector.length !== undefined){
11013         els = selector;
11014     }else{
11015         throw "Invalid selector";
11016     }
11017     if(unique === true){
11018         return new Roo.CompositeElement(els);
11019     }else{
11020         return new Roo.CompositeElementLite(els);
11021     }
11022 };
11023 /**
11024  * Selects elements based on the passed CSS selector to enable working on them as 1.
11025  * @param {String/Array} selector The CSS selector or an array of elements
11026  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11027  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11028  * @return {CompositeElementLite/CompositeElement}
11029  * @member Roo
11030  * @method select
11031  */
11032 Roo.select = Roo.Element.select;
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047 /*
11048  * Based on:
11049  * Ext JS Library 1.1.1
11050  * Copyright(c) 2006-2007, Ext JS, LLC.
11051  *
11052  * Originally Released Under LGPL - original licence link has changed is not relivant.
11053  *
11054  * Fork - LGPL
11055  * <script type="text/javascript">
11056  */
11057
11058
11059
11060 //Notifies Element that fx methods are available
11061 Roo.enableFx = true;
11062
11063 /**
11064  * @class Roo.Fx
11065  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11066  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11067  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11068  * Element effects to work.</p><br/>
11069  *
11070  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11071  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11072  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11073  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11074  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11075  * expected results and should be done with care.</p><br/>
11076  *
11077  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11078  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11079 <pre>
11080 Value  Description
11081 -----  -----------------------------
11082 tl     The top left corner
11083 t      The center of the top edge
11084 tr     The top right corner
11085 l      The center of the left edge
11086 r      The center of the right edge
11087 bl     The bottom left corner
11088 b      The center of the bottom edge
11089 br     The bottom right corner
11090 </pre>
11091  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11092  * below are common options that can be passed to any Fx method.</b>
11093  * @cfg {Function} callback A function called when the effect is finished
11094  * @cfg {Object} scope The scope of the effect function
11095  * @cfg {String} easing A valid Easing value for the effect
11096  * @cfg {String} afterCls A css class to apply after the effect
11097  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11098  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11099  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11100  * effects that end with the element being visually hidden, ignored otherwise)
11101  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11102  * a function which returns such a specification that will be applied to the Element after the effect finishes
11103  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11104  * @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
11105  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11106  */
11107 Roo.Fx = {
11108         /**
11109          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11110          * origin for the slide effect.  This function automatically handles wrapping the element with
11111          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11112          * Usage:
11113          *<pre><code>
11114 // default: slide the element in from the top
11115 el.slideIn();
11116
11117 // custom: slide the element in from the right with a 2-second duration
11118 el.slideIn('r', { duration: 2 });
11119
11120 // common config options shown with default values
11121 el.slideIn('t', {
11122     easing: 'easeOut',
11123     duration: .5
11124 });
11125 </code></pre>
11126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11127          * @param {Object} options (optional) Object literal with any of the Fx config options
11128          * @return {Roo.Element} The Element
11129          */
11130     slideIn : function(anchor, o){
11131         var el = this.getFxEl();
11132         o = o || {};
11133
11134         el.queueFx(o, function(){
11135
11136             anchor = anchor || "t";
11137
11138             // fix display to visibility
11139             this.fixDisplay();
11140
11141             // restore values after effect
11142             var r = this.getFxRestore();
11143             var b = this.getBox();
11144             // fixed size for slide
11145             this.setSize(b);
11146
11147             // wrap if needed
11148             var wrap = this.fxWrap(r.pos, o, "hidden");
11149
11150             var st = this.dom.style;
11151             st.visibility = "visible";
11152             st.position = "absolute";
11153
11154             // clear out temp styles after slide and unwrap
11155             var after = function(){
11156                 el.fxUnwrap(wrap, r.pos, o);
11157                 st.width = r.width;
11158                 st.height = r.height;
11159                 el.afterFx(o);
11160             };
11161             // time to calc the positions
11162             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11163
11164             switch(anchor.toLowerCase()){
11165                 case "t":
11166                     wrap.setSize(b.width, 0);
11167                     st.left = st.bottom = "0";
11168                     a = {height: bh};
11169                 break;
11170                 case "l":
11171                     wrap.setSize(0, b.height);
11172                     st.right = st.top = "0";
11173                     a = {width: bw};
11174                 break;
11175                 case "r":
11176                     wrap.setSize(0, b.height);
11177                     wrap.setX(b.right);
11178                     st.left = st.top = "0";
11179                     a = {width: bw, points: pt};
11180                 break;
11181                 case "b":
11182                     wrap.setSize(b.width, 0);
11183                     wrap.setY(b.bottom);
11184                     st.left = st.top = "0";
11185                     a = {height: bh, points: pt};
11186                 break;
11187                 case "tl":
11188                     wrap.setSize(0, 0);
11189                     st.right = st.bottom = "0";
11190                     a = {width: bw, height: bh};
11191                 break;
11192                 case "bl":
11193                     wrap.setSize(0, 0);
11194                     wrap.setY(b.y+b.height);
11195                     st.right = st.top = "0";
11196                     a = {width: bw, height: bh, points: pt};
11197                 break;
11198                 case "br":
11199                     wrap.setSize(0, 0);
11200                     wrap.setXY([b.right, b.bottom]);
11201                     st.left = st.top = "0";
11202                     a = {width: bw, height: bh, points: pt};
11203                 break;
11204                 case "tr":
11205                     wrap.setSize(0, 0);
11206                     wrap.setX(b.x+b.width);
11207                     st.left = st.bottom = "0";
11208                     a = {width: bw, height: bh, points: pt};
11209                 break;
11210             }
11211             this.dom.style.visibility = "visible";
11212             wrap.show();
11213
11214             arguments.callee.anim = wrap.fxanim(a,
11215                 o,
11216                 'motion',
11217                 .5,
11218                 'easeOut', after);
11219         });
11220         return this;
11221     },
11222     
11223         /**
11224          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11225          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11226          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11227          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11228          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11229          * Usage:
11230          *<pre><code>
11231 // default: slide the element out to the top
11232 el.slideOut();
11233
11234 // custom: slide the element out to the right with a 2-second duration
11235 el.slideOut('r', { duration: 2 });
11236
11237 // common config options shown with default values
11238 el.slideOut('t', {
11239     easing: 'easeOut',
11240     duration: .5,
11241     remove: false,
11242     useDisplay: false
11243 });
11244 </code></pre>
11245          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11246          * @param {Object} options (optional) Object literal with any of the Fx config options
11247          * @return {Roo.Element} The Element
11248          */
11249     slideOut : function(anchor, o){
11250         var el = this.getFxEl();
11251         o = o || {};
11252
11253         el.queueFx(o, function(){
11254
11255             anchor = anchor || "t";
11256
11257             // restore values after effect
11258             var r = this.getFxRestore();
11259             
11260             var b = this.getBox();
11261             // fixed size for slide
11262             this.setSize(b);
11263
11264             // wrap if needed
11265             var wrap = this.fxWrap(r.pos, o, "visible");
11266
11267             var st = this.dom.style;
11268             st.visibility = "visible";
11269             st.position = "absolute";
11270
11271             wrap.setSize(b);
11272
11273             var after = function(){
11274                 if(o.useDisplay){
11275                     el.setDisplayed(false);
11276                 }else{
11277                     el.hide();
11278                 }
11279
11280                 el.fxUnwrap(wrap, r.pos, o);
11281
11282                 st.width = r.width;
11283                 st.height = r.height;
11284
11285                 el.afterFx(o);
11286             };
11287
11288             var a, zero = {to: 0};
11289             switch(anchor.toLowerCase()){
11290                 case "t":
11291                     st.left = st.bottom = "0";
11292                     a = {height: zero};
11293                 break;
11294                 case "l":
11295                     st.right = st.top = "0";
11296                     a = {width: zero};
11297                 break;
11298                 case "r":
11299                     st.left = st.top = "0";
11300                     a = {width: zero, points: {to:[b.right, b.y]}};
11301                 break;
11302                 case "b":
11303                     st.left = st.top = "0";
11304                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11305                 break;
11306                 case "tl":
11307                     st.right = st.bottom = "0";
11308                     a = {width: zero, height: zero};
11309                 break;
11310                 case "bl":
11311                     st.right = st.top = "0";
11312                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11313                 break;
11314                 case "br":
11315                     st.left = st.top = "0";
11316                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11317                 break;
11318                 case "tr":
11319                     st.left = st.bottom = "0";
11320                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11321                 break;
11322             }
11323
11324             arguments.callee.anim = wrap.fxanim(a,
11325                 o,
11326                 'motion',
11327                 .5,
11328                 "easeOut", after);
11329         });
11330         return this;
11331     },
11332
11333         /**
11334          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11335          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11336          * The element must be removed from the DOM using the 'remove' config option if desired.
11337          * Usage:
11338          *<pre><code>
11339 // default
11340 el.puff();
11341
11342 // common config options shown with default values
11343 el.puff({
11344     easing: 'easeOut',
11345     duration: .5,
11346     remove: false,
11347     useDisplay: false
11348 });
11349 </code></pre>
11350          * @param {Object} options (optional) Object literal with any of the Fx config options
11351          * @return {Roo.Element} The Element
11352          */
11353     puff : function(o){
11354         var el = this.getFxEl();
11355         o = o || {};
11356
11357         el.queueFx(o, function(){
11358             this.clearOpacity();
11359             this.show();
11360
11361             // restore values after effect
11362             var r = this.getFxRestore();
11363             var st = this.dom.style;
11364
11365             var after = function(){
11366                 if(o.useDisplay){
11367                     el.setDisplayed(false);
11368                 }else{
11369                     el.hide();
11370                 }
11371
11372                 el.clearOpacity();
11373
11374                 el.setPositioning(r.pos);
11375                 st.width = r.width;
11376                 st.height = r.height;
11377                 st.fontSize = '';
11378                 el.afterFx(o);
11379             };
11380
11381             var width = this.getWidth();
11382             var height = this.getHeight();
11383
11384             arguments.callee.anim = this.fxanim({
11385                     width : {to: this.adjustWidth(width * 2)},
11386                     height : {to: this.adjustHeight(height * 2)},
11387                     points : {by: [-(width * .5), -(height * .5)]},
11388                     opacity : {to: 0},
11389                     fontSize: {to:200, unit: "%"}
11390                 },
11391                 o,
11392                 'motion',
11393                 .5,
11394                 "easeOut", after);
11395         });
11396         return this;
11397     },
11398
11399         /**
11400          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11401          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11402          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11403          * Usage:
11404          *<pre><code>
11405 // default
11406 el.switchOff();
11407
11408 // all config options shown with default values
11409 el.switchOff({
11410     easing: 'easeIn',
11411     duration: .3,
11412     remove: false,
11413     useDisplay: false
11414 });
11415 </code></pre>
11416          * @param {Object} options (optional) Object literal with any of the Fx config options
11417          * @return {Roo.Element} The Element
11418          */
11419     switchOff : function(o){
11420         var el = this.getFxEl();
11421         o = o || {};
11422
11423         el.queueFx(o, function(){
11424             this.clearOpacity();
11425             this.clip();
11426
11427             // restore values after effect
11428             var r = this.getFxRestore();
11429             var st = this.dom.style;
11430
11431             var after = function(){
11432                 if(o.useDisplay){
11433                     el.setDisplayed(false);
11434                 }else{
11435                     el.hide();
11436                 }
11437
11438                 el.clearOpacity();
11439                 el.setPositioning(r.pos);
11440                 st.width = r.width;
11441                 st.height = r.height;
11442
11443                 el.afterFx(o);
11444             };
11445
11446             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11447                 this.clearOpacity();
11448                 (function(){
11449                     this.fxanim({
11450                         height:{to:1},
11451                         points:{by:[0, this.getHeight() * .5]}
11452                     }, o, 'motion', 0.3, 'easeIn', after);
11453                 }).defer(100, this);
11454             });
11455         });
11456         return this;
11457     },
11458
11459     /**
11460      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11461      * changed using the "attr" config option) and then fading back to the original color. If no original
11462      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11463      * Usage:
11464 <pre><code>
11465 // default: highlight background to yellow
11466 el.highlight();
11467
11468 // custom: highlight foreground text to blue for 2 seconds
11469 el.highlight("0000ff", { attr: 'color', duration: 2 });
11470
11471 // common config options shown with default values
11472 el.highlight("ffff9c", {
11473     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11474     endColor: (current color) or "ffffff",
11475     easing: 'easeIn',
11476     duration: 1
11477 });
11478 </code></pre>
11479      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11480      * @param {Object} options (optional) Object literal with any of the Fx config options
11481      * @return {Roo.Element} The Element
11482      */ 
11483     highlight : function(color, o){
11484         var el = this.getFxEl();
11485         o = o || {};
11486
11487         el.queueFx(o, function(){
11488             color = color || "ffff9c";
11489             attr = o.attr || "backgroundColor";
11490
11491             this.clearOpacity();
11492             this.show();
11493
11494             var origColor = this.getColor(attr);
11495             var restoreColor = this.dom.style[attr];
11496             endColor = (o.endColor || origColor) || "ffffff";
11497
11498             var after = function(){
11499                 el.dom.style[attr] = restoreColor;
11500                 el.afterFx(o);
11501             };
11502
11503             var a = {};
11504             a[attr] = {from: color, to: endColor};
11505             arguments.callee.anim = this.fxanim(a,
11506                 o,
11507                 'color',
11508                 1,
11509                 'easeIn', after);
11510         });
11511         return this;
11512     },
11513
11514    /**
11515     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11516     * Usage:
11517 <pre><code>
11518 // default: a single light blue ripple
11519 el.frame();
11520
11521 // custom: 3 red ripples lasting 3 seconds total
11522 el.frame("ff0000", 3, { duration: 3 });
11523
11524 // common config options shown with default values
11525 el.frame("C3DAF9", 1, {
11526     duration: 1 //duration of entire animation (not each individual ripple)
11527     // Note: Easing is not configurable and will be ignored if included
11528 });
11529 </code></pre>
11530     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11531     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11532     * @param {Object} options (optional) Object literal with any of the Fx config options
11533     * @return {Roo.Element} The Element
11534     */
11535     frame : function(color, count, o){
11536         var el = this.getFxEl();
11537         o = o || {};
11538
11539         el.queueFx(o, function(){
11540             color = color || "#C3DAF9";
11541             if(color.length == 6){
11542                 color = "#" + color;
11543             }
11544             count = count || 1;
11545             duration = o.duration || 1;
11546             this.show();
11547
11548             var b = this.getBox();
11549             var animFn = function(){
11550                 var proxy = this.createProxy({
11551
11552                      style:{
11553                         visbility:"hidden",
11554                         position:"absolute",
11555                         "z-index":"35000", // yee haw
11556                         border:"0px solid " + color
11557                      }
11558                   });
11559                 var scale = Roo.isBorderBox ? 2 : 1;
11560                 proxy.animate({
11561                     top:{from:b.y, to:b.y - 20},
11562                     left:{from:b.x, to:b.x - 20},
11563                     borderWidth:{from:0, to:10},
11564                     opacity:{from:1, to:0},
11565                     height:{from:b.height, to:(b.height + (20*scale))},
11566                     width:{from:b.width, to:(b.width + (20*scale))}
11567                 }, duration, function(){
11568                     proxy.remove();
11569                 });
11570                 if(--count > 0){
11571                      animFn.defer((duration/2)*1000, this);
11572                 }else{
11573                     el.afterFx(o);
11574                 }
11575             };
11576             animFn.call(this);
11577         });
11578         return this;
11579     },
11580
11581    /**
11582     * Creates a pause before any subsequent queued effects begin.  If there are
11583     * no effects queued after the pause it will have no effect.
11584     * Usage:
11585 <pre><code>
11586 el.pause(1);
11587 </code></pre>
11588     * @param {Number} seconds The length of time to pause (in seconds)
11589     * @return {Roo.Element} The Element
11590     */
11591     pause : function(seconds){
11592         var el = this.getFxEl();
11593         var o = {};
11594
11595         el.queueFx(o, function(){
11596             setTimeout(function(){
11597                 el.afterFx(o);
11598             }, seconds * 1000);
11599         });
11600         return this;
11601     },
11602
11603    /**
11604     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11605     * using the "endOpacity" config option.
11606     * Usage:
11607 <pre><code>
11608 // default: fade in from opacity 0 to 100%
11609 el.fadeIn();
11610
11611 // custom: fade in from opacity 0 to 75% over 2 seconds
11612 el.fadeIn({ endOpacity: .75, duration: 2});
11613
11614 // common config options shown with default values
11615 el.fadeIn({
11616     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11617     easing: 'easeOut',
11618     duration: .5
11619 });
11620 </code></pre>
11621     * @param {Object} options (optional) Object literal with any of the Fx config options
11622     * @return {Roo.Element} The Element
11623     */
11624     fadeIn : function(o){
11625         var el = this.getFxEl();
11626         o = o || {};
11627         el.queueFx(o, function(){
11628             this.setOpacity(0);
11629             this.fixDisplay();
11630             this.dom.style.visibility = 'visible';
11631             var to = o.endOpacity || 1;
11632             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11633                 o, null, .5, "easeOut", function(){
11634                 if(to == 1){
11635                     this.clearOpacity();
11636                 }
11637                 el.afterFx(o);
11638             });
11639         });
11640         return this;
11641     },
11642
11643    /**
11644     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11645     * using the "endOpacity" config option.
11646     * Usage:
11647 <pre><code>
11648 // default: fade out from the element's current opacity to 0
11649 el.fadeOut();
11650
11651 // custom: fade out from the element's current opacity to 25% over 2 seconds
11652 el.fadeOut({ endOpacity: .25, duration: 2});
11653
11654 // common config options shown with default values
11655 el.fadeOut({
11656     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11657     easing: 'easeOut',
11658     duration: .5
11659     remove: false,
11660     useDisplay: false
11661 });
11662 </code></pre>
11663     * @param {Object} options (optional) Object literal with any of the Fx config options
11664     * @return {Roo.Element} The Element
11665     */
11666     fadeOut : function(o){
11667         var el = this.getFxEl();
11668         o = o || {};
11669         el.queueFx(o, function(){
11670             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11671                 o, null, .5, "easeOut", function(){
11672                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11673                      this.dom.style.display = "none";
11674                 }else{
11675                      this.dom.style.visibility = "hidden";
11676                 }
11677                 this.clearOpacity();
11678                 el.afterFx(o);
11679             });
11680         });
11681         return this;
11682     },
11683
11684    /**
11685     * Animates the transition of an element's dimensions from a starting height/width
11686     * to an ending height/width.
11687     * Usage:
11688 <pre><code>
11689 // change height and width to 100x100 pixels
11690 el.scale(100, 100);
11691
11692 // common config options shown with default values.  The height and width will default to
11693 // the element's existing values if passed as null.
11694 el.scale(
11695     [element's width],
11696     [element's height], {
11697     easing: 'easeOut',
11698     duration: .35
11699 });
11700 </code></pre>
11701     * @param {Number} width  The new width (pass undefined to keep the original width)
11702     * @param {Number} height  The new height (pass undefined to keep the original height)
11703     * @param {Object} options (optional) Object literal with any of the Fx config options
11704     * @return {Roo.Element} The Element
11705     */
11706     scale : function(w, h, o){
11707         this.shift(Roo.apply({}, o, {
11708             width: w,
11709             height: h
11710         }));
11711         return this;
11712     },
11713
11714    /**
11715     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11716     * Any of these properties not specified in the config object will not be changed.  This effect 
11717     * requires that at least one new dimension, position or opacity setting must be passed in on
11718     * the config object in order for the function to have any effect.
11719     * Usage:
11720 <pre><code>
11721 // slide the element horizontally to x position 200 while changing the height and opacity
11722 el.shift({ x: 200, height: 50, opacity: .8 });
11723
11724 // common config options shown with default values.
11725 el.shift({
11726     width: [element's width],
11727     height: [element's height],
11728     x: [element's x position],
11729     y: [element's y position],
11730     opacity: [element's opacity],
11731     easing: 'easeOut',
11732     duration: .35
11733 });
11734 </code></pre>
11735     * @param {Object} options  Object literal with any of the Fx config options
11736     * @return {Roo.Element} The Element
11737     */
11738     shift : function(o){
11739         var el = this.getFxEl();
11740         o = o || {};
11741         el.queueFx(o, function(){
11742             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11743             if(w !== undefined){
11744                 a.width = {to: this.adjustWidth(w)};
11745             }
11746             if(h !== undefined){
11747                 a.height = {to: this.adjustHeight(h)};
11748             }
11749             if(x !== undefined || y !== undefined){
11750                 a.points = {to: [
11751                     x !== undefined ? x : this.getX(),
11752                     y !== undefined ? y : this.getY()
11753                 ]};
11754             }
11755             if(op !== undefined){
11756                 a.opacity = {to: op};
11757             }
11758             if(o.xy !== undefined){
11759                 a.points = {to: o.xy};
11760             }
11761             arguments.callee.anim = this.fxanim(a,
11762                 o, 'motion', .35, "easeOut", function(){
11763                 el.afterFx(o);
11764             });
11765         });
11766         return this;
11767     },
11768
11769         /**
11770          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11771          * ending point of the effect.
11772          * Usage:
11773          *<pre><code>
11774 // default: slide the element downward while fading out
11775 el.ghost();
11776
11777 // custom: slide the element out to the right with a 2-second duration
11778 el.ghost('r', { duration: 2 });
11779
11780 // common config options shown with default values
11781 el.ghost('b', {
11782     easing: 'easeOut',
11783     duration: .5
11784     remove: false,
11785     useDisplay: false
11786 });
11787 </code></pre>
11788          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11789          * @param {Object} options (optional) Object literal with any of the Fx config options
11790          * @return {Roo.Element} The Element
11791          */
11792     ghost : function(anchor, o){
11793         var el = this.getFxEl();
11794         o = o || {};
11795
11796         el.queueFx(o, function(){
11797             anchor = anchor || "b";
11798
11799             // restore values after effect
11800             var r = this.getFxRestore();
11801             var w = this.getWidth(),
11802                 h = this.getHeight();
11803
11804             var st = this.dom.style;
11805
11806             var after = function(){
11807                 if(o.useDisplay){
11808                     el.setDisplayed(false);
11809                 }else{
11810                     el.hide();
11811                 }
11812
11813                 el.clearOpacity();
11814                 el.setPositioning(r.pos);
11815                 st.width = r.width;
11816                 st.height = r.height;
11817
11818                 el.afterFx(o);
11819             };
11820
11821             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11822             switch(anchor.toLowerCase()){
11823                 case "t":
11824                     pt.by = [0, -h];
11825                 break;
11826                 case "l":
11827                     pt.by = [-w, 0];
11828                 break;
11829                 case "r":
11830                     pt.by = [w, 0];
11831                 break;
11832                 case "b":
11833                     pt.by = [0, h];
11834                 break;
11835                 case "tl":
11836                     pt.by = [-w, -h];
11837                 break;
11838                 case "bl":
11839                     pt.by = [-w, h];
11840                 break;
11841                 case "br":
11842                     pt.by = [w, h];
11843                 break;
11844                 case "tr":
11845                     pt.by = [w, -h];
11846                 break;
11847             }
11848
11849             arguments.callee.anim = this.fxanim(a,
11850                 o,
11851                 'motion',
11852                 .5,
11853                 "easeOut", after);
11854         });
11855         return this;
11856     },
11857
11858         /**
11859          * Ensures that all effects queued after syncFx is called on the element are
11860          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11861          * @return {Roo.Element} The Element
11862          */
11863     syncFx : function(){
11864         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11865             block : false,
11866             concurrent : true,
11867             stopFx : false
11868         });
11869         return this;
11870     },
11871
11872         /**
11873          * Ensures that all effects queued after sequenceFx is called on the element are
11874          * run in sequence.  This is the opposite of {@link #syncFx}.
11875          * @return {Roo.Element} The Element
11876          */
11877     sequenceFx : function(){
11878         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11879             block : false,
11880             concurrent : false,
11881             stopFx : false
11882         });
11883         return this;
11884     },
11885
11886         /* @private */
11887     nextFx : function(){
11888         var ef = this.fxQueue[0];
11889         if(ef){
11890             ef.call(this);
11891         }
11892     },
11893
11894         /**
11895          * Returns true if the element has any effects actively running or queued, else returns false.
11896          * @return {Boolean} True if element has active effects, else false
11897          */
11898     hasActiveFx : function(){
11899         return this.fxQueue && this.fxQueue[0];
11900     },
11901
11902         /**
11903          * Stops any running effects and clears the element's internal effects queue if it contains
11904          * any additional effects that haven't started yet.
11905          * @return {Roo.Element} The Element
11906          */
11907     stopFx : function(){
11908         if(this.hasActiveFx()){
11909             var cur = this.fxQueue[0];
11910             if(cur && cur.anim && cur.anim.isAnimated()){
11911                 this.fxQueue = [cur]; // clear out others
11912                 cur.anim.stop(true);
11913             }
11914         }
11915         return this;
11916     },
11917
11918         /* @private */
11919     beforeFx : function(o){
11920         if(this.hasActiveFx() && !o.concurrent){
11921            if(o.stopFx){
11922                this.stopFx();
11923                return true;
11924            }
11925            return false;
11926         }
11927         return true;
11928     },
11929
11930         /**
11931          * Returns true if the element is currently blocking so that no other effect can be queued
11932          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11933          * used to ensure that an effect initiated by a user action runs to completion prior to the
11934          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11935          * @return {Boolean} True if blocking, else false
11936          */
11937     hasFxBlock : function(){
11938         var q = this.fxQueue;
11939         return q && q[0] && q[0].block;
11940     },
11941
11942         /* @private */
11943     queueFx : function(o, fn){
11944         if(!this.fxQueue){
11945             this.fxQueue = [];
11946         }
11947         if(!this.hasFxBlock()){
11948             Roo.applyIf(o, this.fxDefaults);
11949             if(!o.concurrent){
11950                 var run = this.beforeFx(o);
11951                 fn.block = o.block;
11952                 this.fxQueue.push(fn);
11953                 if(run){
11954                     this.nextFx();
11955                 }
11956             }else{
11957                 fn.call(this);
11958             }
11959         }
11960         return this;
11961     },
11962
11963         /* @private */
11964     fxWrap : function(pos, o, vis){
11965         var wrap;
11966         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11967             var wrapXY;
11968             if(o.fixPosition){
11969                 wrapXY = this.getXY();
11970             }
11971             var div = document.createElement("div");
11972             div.style.visibility = vis;
11973             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11974             wrap.setPositioning(pos);
11975             if(wrap.getStyle("position") == "static"){
11976                 wrap.position("relative");
11977             }
11978             this.clearPositioning('auto');
11979             wrap.clip();
11980             wrap.dom.appendChild(this.dom);
11981             if(wrapXY){
11982                 wrap.setXY(wrapXY);
11983             }
11984         }
11985         return wrap;
11986     },
11987
11988         /* @private */
11989     fxUnwrap : function(wrap, pos, o){
11990         this.clearPositioning();
11991         this.setPositioning(pos);
11992         if(!o.wrap){
11993             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11994             wrap.remove();
11995         }
11996     },
11997
11998         /* @private */
11999     getFxRestore : function(){
12000         var st = this.dom.style;
12001         return {pos: this.getPositioning(), width: st.width, height : st.height};
12002     },
12003
12004         /* @private */
12005     afterFx : function(o){
12006         if(o.afterStyle){
12007             this.applyStyles(o.afterStyle);
12008         }
12009         if(o.afterCls){
12010             this.addClass(o.afterCls);
12011         }
12012         if(o.remove === true){
12013             this.remove();
12014         }
12015         Roo.callback(o.callback, o.scope, [this]);
12016         if(!o.concurrent){
12017             this.fxQueue.shift();
12018             this.nextFx();
12019         }
12020     },
12021
12022         /* @private */
12023     getFxEl : function(){ // support for composite element fx
12024         return Roo.get(this.dom);
12025     },
12026
12027         /* @private */
12028     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12029         animType = animType || 'run';
12030         opt = opt || {};
12031         var anim = Roo.lib.Anim[animType](
12032             this.dom, args,
12033             (opt.duration || defaultDur) || .35,
12034             (opt.easing || defaultEase) || 'easeOut',
12035             function(){
12036                 Roo.callback(cb, this);
12037             },
12038             this
12039         );
12040         opt.anim = anim;
12041         return anim;
12042     }
12043 };
12044
12045 // backwords compat
12046 Roo.Fx.resize = Roo.Fx.scale;
12047
12048 //When included, Roo.Fx is automatically applied to Element so that all basic
12049 //effects are available directly via the Element API
12050 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12051  * Based on:
12052  * Ext JS Library 1.1.1
12053  * Copyright(c) 2006-2007, Ext JS, LLC.
12054  *
12055  * Originally Released Under LGPL - original licence link has changed is not relivant.
12056  *
12057  * Fork - LGPL
12058  * <script type="text/javascript">
12059  */
12060
12061
12062 /**
12063  * @class Roo.CompositeElement
12064  * Standard composite class. Creates a Roo.Element for every element in the collection.
12065  * <br><br>
12066  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12067  * actions will be performed on all the elements in this collection.</b>
12068  * <br><br>
12069  * All methods return <i>this</i> and can be chained.
12070  <pre><code>
12071  var els = Roo.select("#some-el div.some-class", true);
12072  // or select directly from an existing element
12073  var el = Roo.get('some-el');
12074  el.select('div.some-class', true);
12075
12076  els.setWidth(100); // all elements become 100 width
12077  els.hide(true); // all elements fade out and hide
12078  // or
12079  els.setWidth(100).hide(true);
12080  </code></pre>
12081  */
12082 Roo.CompositeElement = function(els){
12083     this.elements = [];
12084     this.addElements(els);
12085 };
12086 Roo.CompositeElement.prototype = {
12087     isComposite: true,
12088     addElements : function(els){
12089         if(!els) {
12090             return this;
12091         }
12092         if(typeof els == "string"){
12093             els = Roo.Element.selectorFunction(els);
12094         }
12095         var yels = this.elements;
12096         var index = yels.length-1;
12097         for(var i = 0, len = els.length; i < len; i++) {
12098                 yels[++index] = Roo.get(els[i]);
12099         }
12100         return this;
12101     },
12102
12103     /**
12104     * Clears this composite and adds the elements returned by the passed selector.
12105     * @param {String/Array} els A string CSS selector, an array of elements or an element
12106     * @return {CompositeElement} this
12107     */
12108     fill : function(els){
12109         this.elements = [];
12110         this.add(els);
12111         return this;
12112     },
12113
12114     /**
12115     * Filters this composite to only elements that match the passed selector.
12116     * @param {String} selector A string CSS selector
12117     * @param {Boolean} inverse return inverse filter (not matches)
12118     * @return {CompositeElement} this
12119     */
12120     filter : function(selector, inverse){
12121         var els = [];
12122         inverse = inverse || false;
12123         this.each(function(el){
12124             var match = inverse ? !el.is(selector) : el.is(selector);
12125             if(match){
12126                 els[els.length] = el.dom;
12127             }
12128         });
12129         this.fill(els);
12130         return this;
12131     },
12132
12133     invoke : function(fn, args){
12134         var els = this.elements;
12135         for(var i = 0, len = els.length; i < len; i++) {
12136                 Roo.Element.prototype[fn].apply(els[i], args);
12137         }
12138         return this;
12139     },
12140     /**
12141     * Adds elements to this composite.
12142     * @param {String/Array} els A string CSS selector, an array of elements or an element
12143     * @return {CompositeElement} this
12144     */
12145     add : function(els){
12146         if(typeof els == "string"){
12147             this.addElements(Roo.Element.selectorFunction(els));
12148         }else if(els.length !== undefined){
12149             this.addElements(els);
12150         }else{
12151             this.addElements([els]);
12152         }
12153         return this;
12154     },
12155     /**
12156     * Calls the passed function passing (el, this, index) for each element in this composite.
12157     * @param {Function} fn The function to call
12158     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12159     * @return {CompositeElement} this
12160     */
12161     each : function(fn, scope){
12162         var els = this.elements;
12163         for(var i = 0, len = els.length; i < len; i++){
12164             if(fn.call(scope || els[i], els[i], this, i) === false) {
12165                 break;
12166             }
12167         }
12168         return this;
12169     },
12170
12171     /**
12172      * Returns the Element object at the specified index
12173      * @param {Number} index
12174      * @return {Roo.Element}
12175      */
12176     item : function(index){
12177         return this.elements[index] || null;
12178     },
12179
12180     /**
12181      * Returns the first Element
12182      * @return {Roo.Element}
12183      */
12184     first : function(){
12185         return this.item(0);
12186     },
12187
12188     /**
12189      * Returns the last Element
12190      * @return {Roo.Element}
12191      */
12192     last : function(){
12193         return this.item(this.elements.length-1);
12194     },
12195
12196     /**
12197      * Returns the number of elements in this composite
12198      * @return Number
12199      */
12200     getCount : function(){
12201         return this.elements.length;
12202     },
12203
12204     /**
12205      * Returns true if this composite contains the passed element
12206      * @return Boolean
12207      */
12208     contains : function(el){
12209         return this.indexOf(el) !== -1;
12210     },
12211
12212     /**
12213      * Returns true if this composite contains the passed element
12214      * @return Boolean
12215      */
12216     indexOf : function(el){
12217         return this.elements.indexOf(Roo.get(el));
12218     },
12219
12220
12221     /**
12222     * Removes the specified element(s).
12223     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12224     * or an array of any of those.
12225     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12226     * @return {CompositeElement} this
12227     */
12228     removeElement : function(el, removeDom){
12229         if(el instanceof Array){
12230             for(var i = 0, len = el.length; i < len; i++){
12231                 this.removeElement(el[i]);
12232             }
12233             return this;
12234         }
12235         var index = typeof el == 'number' ? el : this.indexOf(el);
12236         if(index !== -1){
12237             if(removeDom){
12238                 var d = this.elements[index];
12239                 if(d.dom){
12240                     d.remove();
12241                 }else{
12242                     d.parentNode.removeChild(d);
12243                 }
12244             }
12245             this.elements.splice(index, 1);
12246         }
12247         return this;
12248     },
12249
12250     /**
12251     * Replaces the specified element with the passed element.
12252     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12253     * to replace.
12254     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12255     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12256     * @return {CompositeElement} this
12257     */
12258     replaceElement : function(el, replacement, domReplace){
12259         var index = typeof el == 'number' ? el : this.indexOf(el);
12260         if(index !== -1){
12261             if(domReplace){
12262                 this.elements[index].replaceWith(replacement);
12263             }else{
12264                 this.elements.splice(index, 1, Roo.get(replacement))
12265             }
12266         }
12267         return this;
12268     },
12269
12270     /**
12271      * Removes all elements.
12272      */
12273     clear : function(){
12274         this.elements = [];
12275     }
12276 };
12277 (function(){
12278     Roo.CompositeElement.createCall = function(proto, fnName){
12279         if(!proto[fnName]){
12280             proto[fnName] = function(){
12281                 return this.invoke(fnName, arguments);
12282             };
12283         }
12284     };
12285     for(var fnName in Roo.Element.prototype){
12286         if(typeof Roo.Element.prototype[fnName] == "function"){
12287             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12288         }
12289     };
12290 })();
12291 /*
12292  * Based on:
12293  * Ext JS Library 1.1.1
12294  * Copyright(c) 2006-2007, Ext JS, LLC.
12295  *
12296  * Originally Released Under LGPL - original licence link has changed is not relivant.
12297  *
12298  * Fork - LGPL
12299  * <script type="text/javascript">
12300  */
12301
12302 /**
12303  * @class Roo.CompositeElementLite
12304  * @extends Roo.CompositeElement
12305  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12306  <pre><code>
12307  var els = Roo.select("#some-el div.some-class");
12308  // or select directly from an existing element
12309  var el = Roo.get('some-el');
12310  el.select('div.some-class');
12311
12312  els.setWidth(100); // all elements become 100 width
12313  els.hide(true); // all elements fade out and hide
12314  // or
12315  els.setWidth(100).hide(true);
12316  </code></pre><br><br>
12317  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12318  * actions will be performed on all the elements in this collection.</b>
12319  */
12320 Roo.CompositeElementLite = function(els){
12321     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12322     this.el = new Roo.Element.Flyweight();
12323 };
12324 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12325     addElements : function(els){
12326         if(els){
12327             if(els instanceof Array){
12328                 this.elements = this.elements.concat(els);
12329             }else{
12330                 var yels = this.elements;
12331                 var index = yels.length-1;
12332                 for(var i = 0, len = els.length; i < len; i++) {
12333                     yels[++index] = els[i];
12334                 }
12335             }
12336         }
12337         return this;
12338     },
12339     invoke : function(fn, args){
12340         var els = this.elements;
12341         var el = this.el;
12342         for(var i = 0, len = els.length; i < len; i++) {
12343             el.dom = els[i];
12344                 Roo.Element.prototype[fn].apply(el, args);
12345         }
12346         return this;
12347     },
12348     /**
12349      * Returns a flyweight Element of the dom element object at the specified index
12350      * @param {Number} index
12351      * @return {Roo.Element}
12352      */
12353     item : function(index){
12354         if(!this.elements[index]){
12355             return null;
12356         }
12357         this.el.dom = this.elements[index];
12358         return this.el;
12359     },
12360
12361     // fixes scope with flyweight
12362     addListener : function(eventName, handler, scope, opt){
12363         var els = this.elements;
12364         for(var i = 0, len = els.length; i < len; i++) {
12365             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12366         }
12367         return this;
12368     },
12369
12370     /**
12371     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12372     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12373     * a reference to the dom node, use el.dom.</b>
12374     * @param {Function} fn The function to call
12375     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12376     * @return {CompositeElement} this
12377     */
12378     each : function(fn, scope){
12379         var els = this.elements;
12380         var el = this.el;
12381         for(var i = 0, len = els.length; i < len; i++){
12382             el.dom = els[i];
12383                 if(fn.call(scope || el, el, this, i) === false){
12384                 break;
12385             }
12386         }
12387         return this;
12388     },
12389
12390     indexOf : function(el){
12391         return this.elements.indexOf(Roo.getDom(el));
12392     },
12393
12394     replaceElement : function(el, replacement, domReplace){
12395         var index = typeof el == 'number' ? el : this.indexOf(el);
12396         if(index !== -1){
12397             replacement = Roo.getDom(replacement);
12398             if(domReplace){
12399                 var d = this.elements[index];
12400                 d.parentNode.insertBefore(replacement, d);
12401                 d.parentNode.removeChild(d);
12402             }
12403             this.elements.splice(index, 1, replacement);
12404         }
12405         return this;
12406     }
12407 });
12408 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12409
12410 /*
12411  * Based on:
12412  * Ext JS Library 1.1.1
12413  * Copyright(c) 2006-2007, Ext JS, LLC.
12414  *
12415  * Originally Released Under LGPL - original licence link has changed is not relivant.
12416  *
12417  * Fork - LGPL
12418  * <script type="text/javascript">
12419  */
12420
12421  
12422
12423 /**
12424  * @class Roo.data.Connection
12425  * @extends Roo.util.Observable
12426  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12427  * either to a configured URL, or to a URL specified at request time. 
12428  * 
12429  * Requests made by this class are asynchronous, and will return immediately. No data from
12430  * the server will be available to the statement immediately following the {@link #request} call.
12431  * To process returned data, use a callback in the request options object, or an event listener.
12432  * 
12433  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12434  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12435  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12436  * property and, if present, the IFRAME's XML document as the responseXML property.
12437  * 
12438  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12439  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12440  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12441  * standard DOM methods.
12442  * @constructor
12443  * @param {Object} config a configuration object.
12444  */
12445 Roo.data.Connection = function(config){
12446     Roo.apply(this, config);
12447     this.addEvents({
12448         /**
12449          * @event beforerequest
12450          * Fires before a network request is made to retrieve a data object.
12451          * @param {Connection} conn This Connection object.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "beforerequest" : true,
12455         /**
12456          * @event requestcomplete
12457          * Fires if the request was successfully completed.
12458          * @param {Connection} conn This Connection object.
12459          * @param {Object} response The XHR object containing the response data.
12460          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12461          * @param {Object} options The options config object passed to the {@link #request} method.
12462          */
12463         "requestcomplete" : true,
12464         /**
12465          * @event requestexception
12466          * Fires if an error HTTP status was returned from the server.
12467          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12468          * @param {Connection} conn This Connection object.
12469          * @param {Object} response The XHR object containing the response data.
12470          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12471          * @param {Object} options The options config object passed to the {@link #request} method.
12472          */
12473         "requestexception" : true
12474     });
12475     Roo.data.Connection.superclass.constructor.call(this);
12476 };
12477
12478 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12479     /**
12480      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12481      */
12482     /**
12483      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12484      * extra parameters to each request made by this object. (defaults to undefined)
12485      */
12486     /**
12487      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12488      *  to each request made by this object. (defaults to undefined)
12489      */
12490     /**
12491      * @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)
12492      */
12493     /**
12494      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12495      */
12496     timeout : 30000,
12497     /**
12498      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12499      * @type Boolean
12500      */
12501     autoAbort:false,
12502
12503     /**
12504      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12505      * @type Boolean
12506      */
12507     disableCaching: true,
12508
12509     /**
12510      * Sends an HTTP request to a remote server.
12511      * @param {Object} options An object which may contain the following properties:<ul>
12512      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12513      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12514      * request, a url encoded string or a function to call to get either.</li>
12515      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12516      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12517      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12518      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12519      * <li>options {Object} The parameter to the request call.</li>
12520      * <li>success {Boolean} True if the request succeeded.</li>
12521      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12522      * </ul></li>
12523      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12524      * The callback is passed the following parameters:<ul>
12525      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12526      * <li>options {Object} The parameter to the request call.</li>
12527      * </ul></li>
12528      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12529      * The callback is passed the following parameters:<ul>
12530      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12531      * <li>options {Object} The parameter to the request call.</li>
12532      * </ul></li>
12533      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12534      * for the callback function. Defaults to the browser window.</li>
12535      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12536      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12537      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12538      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12539      * params for the post data. Any params will be appended to the URL.</li>
12540      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12541      * </ul>
12542      * @return {Number} transactionId
12543      */
12544     request : function(o){
12545         if(this.fireEvent("beforerequest", this, o) !== false){
12546             var p = o.params;
12547
12548             if(typeof p == "function"){
12549                 p = p.call(o.scope||window, o);
12550             }
12551             if(typeof p == "object"){
12552                 p = Roo.urlEncode(o.params);
12553             }
12554             if(this.extraParams){
12555                 var extras = Roo.urlEncode(this.extraParams);
12556                 p = p ? (p + '&' + extras) : extras;
12557             }
12558
12559             var url = o.url || this.url;
12560             if(typeof url == 'function'){
12561                 url = url.call(o.scope||window, o);
12562             }
12563
12564             if(o.form){
12565                 var form = Roo.getDom(o.form);
12566                 url = url || form.action;
12567
12568                 var enctype = form.getAttribute("enctype");
12569                 
12570                 if (o.formData) {
12571                     return this.doFormDataUpload(o, url);
12572                 }
12573                 
12574                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12575                     return this.doFormUpload(o, p, url);
12576                 }
12577                 var f = Roo.lib.Ajax.serializeForm(form);
12578                 p = p ? (p + '&' + f) : f;
12579             }
12580             
12581             if (!o.form && o.formData) {
12582                 o.formData = o.formData === true ? new FormData() : o.formData;
12583                 for (var k in o.params) {
12584                     o.formData.append(k,o.params[k]);
12585                 }
12586                     
12587                 return this.doFormDataUpload(o, url);
12588             }
12589             
12590
12591             var hs = o.headers;
12592             if(this.defaultHeaders){
12593                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12594                 if(!o.headers){
12595                     o.headers = hs;
12596                 }
12597             }
12598
12599             var cb = {
12600                 success: this.handleResponse,
12601                 failure: this.handleFailure,
12602                 scope: this,
12603                 argument: {options: o},
12604                 timeout : o.timeout || this.timeout
12605             };
12606
12607             var method = o.method||this.method||(p ? "POST" : "GET");
12608
12609             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12610                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12611             }
12612
12613             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12614                 if(o.autoAbort){
12615                     this.abort();
12616                 }
12617             }else if(this.autoAbort !== false){
12618                 this.abort();
12619             }
12620
12621             if((method == 'GET' && p) || o.xmlData){
12622                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12623                 p = '';
12624             }
12625             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12626             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12627             Roo.lib.Ajax.useDefaultHeader == true;
12628             return this.transId;
12629         }else{
12630             Roo.callback(o.callback, o.scope, [o, null, null]);
12631             return null;
12632         }
12633     },
12634
12635     /**
12636      * Determine whether this object has a request outstanding.
12637      * @param {Number} transactionId (Optional) defaults to the last transaction
12638      * @return {Boolean} True if there is an outstanding request.
12639      */
12640     isLoading : function(transId){
12641         if(transId){
12642             return Roo.lib.Ajax.isCallInProgress(transId);
12643         }else{
12644             return this.transId ? true : false;
12645         }
12646     },
12647
12648     /**
12649      * Aborts any outstanding request.
12650      * @param {Number} transactionId (Optional) defaults to the last transaction
12651      */
12652     abort : function(transId){
12653         if(transId || this.isLoading()){
12654             Roo.lib.Ajax.abort(transId || this.transId);
12655         }
12656     },
12657
12658     // private
12659     handleResponse : function(response){
12660         this.transId = false;
12661         var options = response.argument.options;
12662         response.argument = options ? options.argument : null;
12663         this.fireEvent("requestcomplete", this, response, options);
12664         Roo.callback(options.success, options.scope, [response, options]);
12665         Roo.callback(options.callback, options.scope, [options, true, response]);
12666     },
12667
12668     // private
12669     handleFailure : function(response, e){
12670         this.transId = false;
12671         var options = response.argument.options;
12672         response.argument = options ? options.argument : null;
12673         this.fireEvent("requestexception", this, response, options, e);
12674         Roo.callback(options.failure, options.scope, [response, options]);
12675         Roo.callback(options.callback, options.scope, [options, false, response]);
12676     },
12677
12678     // private
12679     doFormUpload : function(o, ps, url){
12680         var id = Roo.id();
12681         var frame = document.createElement('iframe');
12682         frame.id = id;
12683         frame.name = id;
12684         frame.className = 'x-hidden';
12685         if(Roo.isIE){
12686             frame.src = Roo.SSL_SECURE_URL;
12687         }
12688         document.body.appendChild(frame);
12689
12690         if(Roo.isIE){
12691            document.frames[id].name = id;
12692         }
12693
12694         var form = Roo.getDom(o.form);
12695         form.target = id;
12696         form.method = 'POST';
12697         form.enctype = form.encoding = 'multipart/form-data';
12698         if(url){
12699             form.action = url;
12700         }
12701
12702         var hiddens, hd;
12703         if(ps){ // add dynamic params
12704             hiddens = [];
12705             ps = Roo.urlDecode(ps, false);
12706             for(var k in ps){
12707                 if(ps.hasOwnProperty(k)){
12708                     hd = document.createElement('input');
12709                     hd.type = 'hidden';
12710                     hd.name = k;
12711                     hd.value = ps[k];
12712                     form.appendChild(hd);
12713                     hiddens.push(hd);
12714                 }
12715             }
12716         }
12717
12718         function cb(){
12719             var r = {  // bogus response object
12720                 responseText : '',
12721                 responseXML : null
12722             };
12723
12724             r.argument = o ? o.argument : null;
12725
12726             try { //
12727                 var doc;
12728                 if(Roo.isIE){
12729                     doc = frame.contentWindow.document;
12730                 }else {
12731                     doc = (frame.contentDocument || window.frames[id].document);
12732                 }
12733                 if(doc && doc.body){
12734                     r.responseText = doc.body.innerHTML;
12735                 }
12736                 if(doc && doc.XMLDocument){
12737                     r.responseXML = doc.XMLDocument;
12738                 }else {
12739                     r.responseXML = doc;
12740                 }
12741             }
12742             catch(e) {
12743                 // ignore
12744             }
12745
12746             Roo.EventManager.removeListener(frame, 'load', cb, this);
12747
12748             this.fireEvent("requestcomplete", this, r, o);
12749             Roo.callback(o.success, o.scope, [r, o]);
12750             Roo.callback(o.callback, o.scope, [o, true, r]);
12751
12752             setTimeout(function(){document.body.removeChild(frame);}, 100);
12753         }
12754
12755         Roo.EventManager.on(frame, 'load', cb, this);
12756         form.submit();
12757
12758         if(hiddens){ // remove dynamic params
12759             for(var i = 0, len = hiddens.length; i < len; i++){
12760                 form.removeChild(hiddens[i]);
12761             }
12762         }
12763     },
12764     // this is a 'formdata version???'
12765     
12766     
12767     doFormDataUpload : function(o,  url)
12768     {
12769         var formData;
12770         if (o.form) {
12771             var form =  Roo.getDom(o.form);
12772             form.enctype = form.encoding = 'multipart/form-data';
12773             formData = o.formData === true ? new FormData(form) : o.formData;
12774         } else {
12775             formData = o.formData === true ? new FormData() : o.formData;
12776         }
12777         
12778       
12779         var cb = {
12780             success: this.handleResponse,
12781             failure: this.handleFailure,
12782             scope: this,
12783             argument: {options: o},
12784             timeout : o.timeout || this.timeout
12785         };
12786  
12787         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12788             if(o.autoAbort){
12789                 this.abort();
12790             }
12791         }else if(this.autoAbort !== false){
12792             this.abort();
12793         }
12794
12795         //Roo.lib.Ajax.defaultPostHeader = null;
12796         Roo.lib.Ajax.useDefaultHeader = false;
12797         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12798         Roo.lib.Ajax.useDefaultHeader = true;
12799  
12800          
12801     }
12802     
12803 });
12804 /*
12805  * Based on:
12806  * Ext JS Library 1.1.1
12807  * Copyright(c) 2006-2007, Ext JS, LLC.
12808  *
12809  * Originally Released Under LGPL - original licence link has changed is not relivant.
12810  *
12811  * Fork - LGPL
12812  * <script type="text/javascript">
12813  */
12814  
12815 /**
12816  * Global Ajax request class.
12817  * 
12818  * @class Roo.Ajax
12819  * @extends Roo.data.Connection
12820  * @static
12821  * 
12822  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12823  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12824  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12825  * @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)
12826  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12827  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12828  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12829  */
12830 Roo.Ajax = new Roo.data.Connection({
12831     // fix up the docs
12832     /**
12833      * @scope Roo.Ajax
12834      * @type {Boolear} 
12835      */
12836     autoAbort : false,
12837
12838     /**
12839      * Serialize the passed form into a url encoded string
12840      * @scope Roo.Ajax
12841      * @param {String/HTMLElement} form
12842      * @return {String}
12843      */
12844     serializeForm : function(form){
12845         return Roo.lib.Ajax.serializeForm(form);
12846     }
12847 });/*
12848  * Based on:
12849  * Ext JS Library 1.1.1
12850  * Copyright(c) 2006-2007, Ext JS, LLC.
12851  *
12852  * Originally Released Under LGPL - original licence link has changed is not relivant.
12853  *
12854  * Fork - LGPL
12855  * <script type="text/javascript">
12856  */
12857
12858  
12859 /**
12860  * @class Roo.UpdateManager
12861  * @extends Roo.util.Observable
12862  * Provides AJAX-style update for Element object.<br><br>
12863  * Usage:<br>
12864  * <pre><code>
12865  * // Get it from a Roo.Element object
12866  * var el = Roo.get("foo");
12867  * var mgr = el.getUpdateManager();
12868  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12869  * ...
12870  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12871  * <br>
12872  * // or directly (returns the same UpdateManager instance)
12873  * var mgr = new Roo.UpdateManager("myElementId");
12874  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12875  * mgr.on("update", myFcnNeedsToKnow);
12876  * <br>
12877    // short handed call directly from the element object
12878    Roo.get("foo").load({
12879         url: "bar.php",
12880         scripts:true,
12881         params: "for=bar",
12882         text: "Loading Foo..."
12883    });
12884  * </code></pre>
12885  * @constructor
12886  * Create new UpdateManager directly.
12887  * @param {String/HTMLElement/Roo.Element} el The element to update
12888  * @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).
12889  */
12890 Roo.UpdateManager = function(el, forceNew){
12891     el = Roo.get(el);
12892     if(!forceNew && el.updateManager){
12893         return el.updateManager;
12894     }
12895     /**
12896      * The Element object
12897      * @type Roo.Element
12898      */
12899     this.el = el;
12900     /**
12901      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12902      * @type String
12903      */
12904     this.defaultUrl = null;
12905
12906     this.addEvents({
12907         /**
12908          * @event beforeupdate
12909          * Fired before an update is made, return false from your handler and the update is cancelled.
12910          * @param {Roo.Element} el
12911          * @param {String/Object/Function} url
12912          * @param {String/Object} params
12913          */
12914         "beforeupdate": true,
12915         /**
12916          * @event update
12917          * Fired after successful update is made.
12918          * @param {Roo.Element} el
12919          * @param {Object} oResponseObject The response Object
12920          */
12921         "update": true,
12922         /**
12923          * @event failure
12924          * Fired on update failure.
12925          * @param {Roo.Element} el
12926          * @param {Object} oResponseObject The response Object
12927          */
12928         "failure": true
12929     });
12930     var d = Roo.UpdateManager.defaults;
12931     /**
12932      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12933      * @type String
12934      */
12935     this.sslBlankUrl = d.sslBlankUrl;
12936     /**
12937      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12938      * @type Boolean
12939      */
12940     this.disableCaching = d.disableCaching;
12941     /**
12942      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12943      * @type String
12944      */
12945     this.indicatorText = d.indicatorText;
12946     /**
12947      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12948      * @type String
12949      */
12950     this.showLoadIndicator = d.showLoadIndicator;
12951     /**
12952      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12953      * @type Number
12954      */
12955     this.timeout = d.timeout;
12956
12957     /**
12958      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12959      * @type Boolean
12960      */
12961     this.loadScripts = d.loadScripts;
12962
12963     /**
12964      * Transaction object of current executing transaction
12965      */
12966     this.transaction = null;
12967
12968     /**
12969      * @private
12970      */
12971     this.autoRefreshProcId = null;
12972     /**
12973      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12974      * @type Function
12975      */
12976     this.refreshDelegate = this.refresh.createDelegate(this);
12977     /**
12978      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12979      * @type Function
12980      */
12981     this.updateDelegate = this.update.createDelegate(this);
12982     /**
12983      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12984      * @type Function
12985      */
12986     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12987     /**
12988      * @private
12989      */
12990     this.successDelegate = this.processSuccess.createDelegate(this);
12991     /**
12992      * @private
12993      */
12994     this.failureDelegate = this.processFailure.createDelegate(this);
12995
12996     if(!this.renderer){
12997      /**
12998       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12999       */
13000     this.renderer = new Roo.UpdateManager.BasicRenderer();
13001     }
13002     
13003     Roo.UpdateManager.superclass.constructor.call(this);
13004 };
13005
13006 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13007     /**
13008      * Get the Element this UpdateManager is bound to
13009      * @return {Roo.Element} The element
13010      */
13011     getEl : function(){
13012         return this.el;
13013     },
13014     /**
13015      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13016      * @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:
13017 <pre><code>
13018 um.update({<br/>
13019     url: "your-url.php",<br/>
13020     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13021     callback: yourFunction,<br/>
13022     scope: yourObject, //(optional scope)  <br/>
13023     discardUrl: false, <br/>
13024     nocache: false,<br/>
13025     text: "Loading...",<br/>
13026     timeout: 30,<br/>
13027     scripts: false<br/>
13028 });
13029 </code></pre>
13030      * The only required property is url. The optional properties nocache, text and scripts
13031      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13032      * @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}
13033      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13034      * @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.
13035      */
13036     update : function(url, params, callback, discardUrl){
13037         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13038             var method = this.method,
13039                 cfg;
13040             if(typeof url == "object"){ // must be config object
13041                 cfg = url;
13042                 url = cfg.url;
13043                 params = params || cfg.params;
13044                 callback = callback || cfg.callback;
13045                 discardUrl = discardUrl || cfg.discardUrl;
13046                 if(callback && cfg.scope){
13047                     callback = callback.createDelegate(cfg.scope);
13048                 }
13049                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13050                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13051                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13052                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13053                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13054             }
13055             this.showLoading();
13056             if(!discardUrl){
13057                 this.defaultUrl = url;
13058             }
13059             if(typeof url == "function"){
13060                 url = url.call(this);
13061             }
13062
13063             method = method || (params ? "POST" : "GET");
13064             if(method == "GET"){
13065                 url = this.prepareUrl(url);
13066             }
13067
13068             var o = Roo.apply(cfg ||{}, {
13069                 url : url,
13070                 params: params,
13071                 success: this.successDelegate,
13072                 failure: this.failureDelegate,
13073                 callback: undefined,
13074                 timeout: (this.timeout*1000),
13075                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13076             });
13077             Roo.log("updated manager called with timeout of " + o.timeout);
13078             this.transaction = Roo.Ajax.request(o);
13079         }
13080     },
13081
13082     /**
13083      * 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.
13084      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13085      * @param {String/HTMLElement} form The form Id or form element
13086      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13087      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13088      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13089      */
13090     formUpdate : function(form, url, reset, callback){
13091         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13092             if(typeof url == "function"){
13093                 url = url.call(this);
13094             }
13095             form = Roo.getDom(form);
13096             this.transaction = Roo.Ajax.request({
13097                 form: form,
13098                 url:url,
13099                 success: this.successDelegate,
13100                 failure: this.failureDelegate,
13101                 timeout: (this.timeout*1000),
13102                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13103             });
13104             this.showLoading.defer(1, this);
13105         }
13106     },
13107
13108     /**
13109      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13110      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13111      */
13112     refresh : function(callback){
13113         if(this.defaultUrl == null){
13114             return;
13115         }
13116         this.update(this.defaultUrl, null, callback, true);
13117     },
13118
13119     /**
13120      * Set this element to auto refresh.
13121      * @param {Number} interval How often to update (in seconds).
13122      * @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)
13123      * @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}
13124      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13125      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13126      */
13127     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13128         if(refreshNow){
13129             this.update(url || this.defaultUrl, params, callback, true);
13130         }
13131         if(this.autoRefreshProcId){
13132             clearInterval(this.autoRefreshProcId);
13133         }
13134         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13135     },
13136
13137     /**
13138      * Stop auto refresh on this element.
13139      */
13140      stopAutoRefresh : function(){
13141         if(this.autoRefreshProcId){
13142             clearInterval(this.autoRefreshProcId);
13143             delete this.autoRefreshProcId;
13144         }
13145     },
13146
13147     isAutoRefreshing : function(){
13148        return this.autoRefreshProcId ? true : false;
13149     },
13150     /**
13151      * Called to update the element to "Loading" state. Override to perform custom action.
13152      */
13153     showLoading : function(){
13154         if(this.showLoadIndicator){
13155             this.el.update(this.indicatorText);
13156         }
13157     },
13158
13159     /**
13160      * Adds unique parameter to query string if disableCaching = true
13161      * @private
13162      */
13163     prepareUrl : function(url){
13164         if(this.disableCaching){
13165             var append = "_dc=" + (new Date().getTime());
13166             if(url.indexOf("?") !== -1){
13167                 url += "&" + append;
13168             }else{
13169                 url += "?" + append;
13170             }
13171         }
13172         return url;
13173     },
13174
13175     /**
13176      * @private
13177      */
13178     processSuccess : function(response){
13179         this.transaction = null;
13180         if(response.argument.form && response.argument.reset){
13181             try{ // put in try/catch since some older FF releases had problems with this
13182                 response.argument.form.reset();
13183             }catch(e){}
13184         }
13185         if(this.loadScripts){
13186             this.renderer.render(this.el, response, this,
13187                 this.updateComplete.createDelegate(this, [response]));
13188         }else{
13189             this.renderer.render(this.el, response, this);
13190             this.updateComplete(response);
13191         }
13192     },
13193
13194     updateComplete : function(response){
13195         this.fireEvent("update", this.el, response);
13196         if(typeof response.argument.callback == "function"){
13197             response.argument.callback(this.el, true, response);
13198         }
13199     },
13200
13201     /**
13202      * @private
13203      */
13204     processFailure : function(response){
13205         this.transaction = null;
13206         this.fireEvent("failure", this.el, response);
13207         if(typeof response.argument.callback == "function"){
13208             response.argument.callback(this.el, false, response);
13209         }
13210     },
13211
13212     /**
13213      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13214      * @param {Object} renderer The object implementing the render() method
13215      */
13216     setRenderer : function(renderer){
13217         this.renderer = renderer;
13218     },
13219
13220     getRenderer : function(){
13221        return this.renderer;
13222     },
13223
13224     /**
13225      * Set the defaultUrl used for updates
13226      * @param {String/Function} defaultUrl The url or a function to call to get the url
13227      */
13228     setDefaultUrl : function(defaultUrl){
13229         this.defaultUrl = defaultUrl;
13230     },
13231
13232     /**
13233      * Aborts the executing transaction
13234      */
13235     abort : function(){
13236         if(this.transaction){
13237             Roo.Ajax.abort(this.transaction);
13238         }
13239     },
13240
13241     /**
13242      * Returns true if an update is in progress
13243      * @return {Boolean}
13244      */
13245     isUpdating : function(){
13246         if(this.transaction){
13247             return Roo.Ajax.isLoading(this.transaction);
13248         }
13249         return false;
13250     }
13251 });
13252
13253 /**
13254  * @class Roo.UpdateManager.defaults
13255  * @static (not really - but it helps the doc tool)
13256  * The defaults collection enables customizing the default properties of UpdateManager
13257  */
13258    Roo.UpdateManager.defaults = {
13259        /**
13260          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13261          * @type Number
13262          */
13263          timeout : 30,
13264
13265          /**
13266          * True to process scripts by default (Defaults to false).
13267          * @type Boolean
13268          */
13269         loadScripts : false,
13270
13271         /**
13272         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13273         * @type String
13274         */
13275         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13276         /**
13277          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13278          * @type Boolean
13279          */
13280         disableCaching : false,
13281         /**
13282          * Whether to show indicatorText when loading (Defaults to true).
13283          * @type Boolean
13284          */
13285         showLoadIndicator : true,
13286         /**
13287          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13288          * @type String
13289          */
13290         indicatorText : '<div class="loading-indicator">Loading...</div>'
13291    };
13292
13293 /**
13294  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13295  *Usage:
13296  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13297  * @param {String/HTMLElement/Roo.Element} el The element to update
13298  * @param {String} url The url
13299  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13300  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13301  * @static
13302  * @deprecated
13303  * @member Roo.UpdateManager
13304  */
13305 Roo.UpdateManager.updateElement = function(el, url, params, options){
13306     var um = Roo.get(el, true).getUpdateManager();
13307     Roo.apply(um, options);
13308     um.update(url, params, options ? options.callback : null);
13309 };
13310 // alias for backwards compat
13311 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13312 /**
13313  * @class Roo.UpdateManager.BasicRenderer
13314  * Default Content renderer. Updates the elements innerHTML with the responseText.
13315  */
13316 Roo.UpdateManager.BasicRenderer = function(){};
13317
13318 Roo.UpdateManager.BasicRenderer.prototype = {
13319     /**
13320      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13321      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13322      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13323      * @param {Roo.Element} el The element being rendered
13324      * @param {Object} response The YUI Connect response object
13325      * @param {UpdateManager} updateManager The calling update manager
13326      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13327      */
13328      render : function(el, response, updateManager, callback){
13329         el.update(response.responseText, updateManager.loadScripts, callback);
13330     }
13331 };
13332 /*
13333  * Based on:
13334  * Roo JS
13335  * (c)) Alan Knowles
13336  * Licence : LGPL
13337  */
13338
13339
13340 /**
13341  * @class Roo.DomTemplate
13342  * @extends Roo.Template
13343  * An effort at a dom based template engine..
13344  *
13345  * Similar to XTemplate, except it uses dom parsing to create the template..
13346  *
13347  * Supported features:
13348  *
13349  *  Tags:
13350
13351 <pre><code>
13352       {a_variable} - output encoded.
13353       {a_variable.format:("Y-m-d")} - call a method on the variable
13354       {a_variable:raw} - unencoded output
13355       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13356       {a_variable:this.method_on_template(...)} - call a method on the template object.
13357  
13358 </code></pre>
13359  *  The tpl tag:
13360 <pre><code>
13361         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13362         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13363         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13364         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13365   
13366 </code></pre>
13367  *      
13368  */
13369 Roo.DomTemplate = function()
13370 {
13371      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13372      if (this.html) {
13373         this.compile();
13374      }
13375 };
13376
13377
13378 Roo.extend(Roo.DomTemplate, Roo.Template, {
13379     /**
13380      * id counter for sub templates.
13381      */
13382     id : 0,
13383     /**
13384      * flag to indicate if dom parser is inside a pre,
13385      * it will strip whitespace if not.
13386      */
13387     inPre : false,
13388     
13389     /**
13390      * The various sub templates
13391      */
13392     tpls : false,
13393     
13394     
13395     
13396     /**
13397      *
13398      * basic tag replacing syntax
13399      * WORD:WORD()
13400      *
13401      * // you can fake an object call by doing this
13402      *  x.t:(test,tesT) 
13403      * 
13404      */
13405     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13406     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13407     
13408     iterChild : function (node, method) {
13409         
13410         var oldPre = this.inPre;
13411         if (node.tagName == 'PRE') {
13412             this.inPre = true;
13413         }
13414         for( var i = 0; i < node.childNodes.length; i++) {
13415             method.call(this, node.childNodes[i]);
13416         }
13417         this.inPre = oldPre;
13418     },
13419     
13420     
13421     
13422     /**
13423      * compile the template
13424      *
13425      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13426      *
13427      */
13428     compile: function()
13429     {
13430         var s = this.html;
13431         
13432         // covert the html into DOM...
13433         var doc = false;
13434         var div =false;
13435         try {
13436             doc = document.implementation.createHTMLDocument("");
13437             doc.documentElement.innerHTML =   this.html  ;
13438             div = doc.documentElement;
13439         } catch (e) {
13440             // old IE... - nasty -- it causes all sorts of issues.. with
13441             // images getting pulled from server..
13442             div = document.createElement('div');
13443             div.innerHTML = this.html;
13444         }
13445         //doc.documentElement.innerHTML = htmlBody
13446          
13447         
13448         
13449         this.tpls = [];
13450         var _t = this;
13451         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13452         
13453         var tpls = this.tpls;
13454         
13455         // create a top level template from the snippet..
13456         
13457         //Roo.log(div.innerHTML);
13458         
13459         var tpl = {
13460             uid : 'master',
13461             id : this.id++,
13462             attr : false,
13463             value : false,
13464             body : div.innerHTML,
13465             
13466             forCall : false,
13467             execCall : false,
13468             dom : div,
13469             isTop : true
13470             
13471         };
13472         tpls.unshift(tpl);
13473         
13474         
13475         // compile them...
13476         this.tpls = [];
13477         Roo.each(tpls, function(tp){
13478             this.compileTpl(tp);
13479             this.tpls[tp.id] = tp;
13480         }, this);
13481         
13482         this.master = tpls[0];
13483         return this;
13484         
13485         
13486     },
13487     
13488     compileNode : function(node, istop) {
13489         // test for
13490         //Roo.log(node);
13491         
13492         
13493         // skip anything not a tag..
13494         if (node.nodeType != 1) {
13495             if (node.nodeType == 3 && !this.inPre) {
13496                 // reduce white space..
13497                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13498                 
13499             }
13500             return;
13501         }
13502         
13503         var tpl = {
13504             uid : false,
13505             id : false,
13506             attr : false,
13507             value : false,
13508             body : '',
13509             
13510             forCall : false,
13511             execCall : false,
13512             dom : false,
13513             isTop : istop
13514             
13515             
13516         };
13517         
13518         
13519         switch(true) {
13520             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13521             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13522             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13523             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13524             // no default..
13525         }
13526         
13527         
13528         if (!tpl.attr) {
13529             // just itterate children..
13530             this.iterChild(node,this.compileNode);
13531             return;
13532         }
13533         tpl.uid = this.id++;
13534         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13535         node.removeAttribute('roo-'+ tpl.attr);
13536         if (tpl.attr != 'name') {
13537             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13538             node.parentNode.replaceChild(placeholder,  node);
13539         } else {
13540             
13541             var placeholder =  document.createElement('span');
13542             placeholder.className = 'roo-tpl-' + tpl.value;
13543             node.parentNode.replaceChild(placeholder,  node);
13544         }
13545         
13546         // parent now sees '{domtplXXXX}
13547         this.iterChild(node,this.compileNode);
13548         
13549         // we should now have node body...
13550         var div = document.createElement('div');
13551         div.appendChild(node);
13552         tpl.dom = node;
13553         // this has the unfortunate side effect of converting tagged attributes
13554         // eg. href="{...}" into %7C...%7D
13555         // this has been fixed by searching for those combo's although it's a bit hacky..
13556         
13557         
13558         tpl.body = div.innerHTML;
13559         
13560         
13561          
13562         tpl.id = tpl.uid;
13563         switch(tpl.attr) {
13564             case 'for' :
13565                 switch (tpl.value) {
13566                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13567                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13568                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13569                 }
13570                 break;
13571             
13572             case 'exec':
13573                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13574                 break;
13575             
13576             case 'if':     
13577                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13578                 break;
13579             
13580             case 'name':
13581                 tpl.id  = tpl.value; // replace non characters???
13582                 break;
13583             
13584         }
13585         
13586         
13587         this.tpls.push(tpl);
13588         
13589         
13590         
13591     },
13592     
13593     
13594     
13595     
13596     /**
13597      * Compile a segment of the template into a 'sub-template'
13598      *
13599      * 
13600      * 
13601      *
13602      */
13603     compileTpl : function(tpl)
13604     {
13605         var fm = Roo.util.Format;
13606         var useF = this.disableFormats !== true;
13607         
13608         var sep = Roo.isGecko ? "+\n" : ",\n";
13609         
13610         var undef = function(str) {
13611             Roo.debug && Roo.log("Property not found :"  + str);
13612             return '';
13613         };
13614           
13615         //Roo.log(tpl.body);
13616         
13617         
13618         
13619         var fn = function(m, lbrace, name, format, args)
13620         {
13621             //Roo.log("ARGS");
13622             //Roo.log(arguments);
13623             args = args ? args.replace(/\\'/g,"'") : args;
13624             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13625             if (typeof(format) == 'undefined') {
13626                 format =  'htmlEncode'; 
13627             }
13628             if (format == 'raw' ) {
13629                 format = false;
13630             }
13631             
13632             if(name.substr(0, 6) == 'domtpl'){
13633                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13634             }
13635             
13636             // build an array of options to determine if value is undefined..
13637             
13638             // basically get 'xxxx.yyyy' then do
13639             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13640             //    (function () { Roo.log("Property not found"); return ''; })() :
13641             //    ......
13642             
13643             var udef_ar = [];
13644             var lookfor = '';
13645             Roo.each(name.split('.'), function(st) {
13646                 lookfor += (lookfor.length ? '.': '') + st;
13647                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13648             });
13649             
13650             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13651             
13652             
13653             if(format && useF){
13654                 
13655                 args = args ? ',' + args : "";
13656                  
13657                 if(format.substr(0, 5) != "this."){
13658                     format = "fm." + format + '(';
13659                 }else{
13660                     format = 'this.call("'+ format.substr(5) + '", ';
13661                     args = ", values";
13662                 }
13663                 
13664                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13665             }
13666              
13667             if (args && args.length) {
13668                 // called with xxyx.yuu:(test,test)
13669                 // change to ()
13670                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13671             }
13672             // raw.. - :raw modifier..
13673             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13674             
13675         };
13676         var body;
13677         // branched to use + in gecko and [].join() in others
13678         if(Roo.isGecko){
13679             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13680                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13681                     "';};};";
13682         }else{
13683             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13684             body.push(tpl.body.replace(/(\r\n|\n)/g,
13685                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13686             body.push("'].join('');};};");
13687             body = body.join('');
13688         }
13689         
13690         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13691        
13692         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13693         eval(body);
13694         
13695         return this;
13696     },
13697      
13698     /**
13699      * same as applyTemplate, except it's done to one of the subTemplates
13700      * when using named templates, you can do:
13701      *
13702      * var str = pl.applySubTemplate('your-name', values);
13703      *
13704      * 
13705      * @param {Number} id of the template
13706      * @param {Object} values to apply to template
13707      * @param {Object} parent (normaly the instance of this object)
13708      */
13709     applySubTemplate : function(id, values, parent)
13710     {
13711         
13712         
13713         var t = this.tpls[id];
13714         
13715         
13716         try { 
13717             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13718                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13719                 return '';
13720             }
13721         } catch(e) {
13722             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13723             Roo.log(values);
13724           
13725             return '';
13726         }
13727         try { 
13728             
13729             if(t.execCall && t.execCall.call(this, values, parent)){
13730                 return '';
13731             }
13732         } catch(e) {
13733             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13734             Roo.log(values);
13735             return '';
13736         }
13737         
13738         try {
13739             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13740             parent = t.target ? values : parent;
13741             if(t.forCall && vs instanceof Array){
13742                 var buf = [];
13743                 for(var i = 0, len = vs.length; i < len; i++){
13744                     try {
13745                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13746                     } catch (e) {
13747                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13748                         Roo.log(e.body);
13749                         //Roo.log(t.compiled);
13750                         Roo.log(vs[i]);
13751                     }   
13752                 }
13753                 return buf.join('');
13754             }
13755         } catch (e) {
13756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13757             Roo.log(values);
13758             return '';
13759         }
13760         try {
13761             return t.compiled.call(this, vs, parent);
13762         } catch (e) {
13763             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13764             Roo.log(e.body);
13765             //Roo.log(t.compiled);
13766             Roo.log(values);
13767             return '';
13768         }
13769     },
13770
13771    
13772
13773     applyTemplate : function(values){
13774         return this.master.compiled.call(this, values, {});
13775         //var s = this.subs;
13776     },
13777
13778     apply : function(){
13779         return this.applyTemplate.apply(this, arguments);
13780     }
13781
13782  });
13783
13784 Roo.DomTemplate.from = function(el){
13785     el = Roo.getDom(el);
13786     return new Roo.Domtemplate(el.value || el.innerHTML);
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798 /**
13799  * @class Roo.util.DelayedTask
13800  * Provides a convenient method of performing setTimeout where a new
13801  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13802  * You can use this class to buffer
13803  * the keypress events for a certain number of milliseconds, and perform only if they stop
13804  * for that amount of time.
13805  * @constructor The parameters to this constructor serve as defaults and are not required.
13806  * @param {Function} fn (optional) The default function to timeout
13807  * @param {Object} scope (optional) The default scope of that timeout
13808  * @param {Array} args (optional) The default Array of arguments
13809  */
13810 Roo.util.DelayedTask = function(fn, scope, args){
13811     var id = null, d, t;
13812
13813     var call = function(){
13814         var now = new Date().getTime();
13815         if(now - t >= d){
13816             clearInterval(id);
13817             id = null;
13818             fn.apply(scope, args || []);
13819         }
13820     };
13821     /**
13822      * Cancels any pending timeout and queues a new one
13823      * @param {Number} delay The milliseconds to delay
13824      * @param {Function} newFn (optional) Overrides function passed to constructor
13825      * @param {Object} newScope (optional) Overrides scope passed to constructor
13826      * @param {Array} newArgs (optional) Overrides args passed to constructor
13827      */
13828     this.delay = function(delay, newFn, newScope, newArgs){
13829         if(id && delay != d){
13830             this.cancel();
13831         }
13832         d = delay;
13833         t = new Date().getTime();
13834         fn = newFn || fn;
13835         scope = newScope || scope;
13836         args = newArgs || args;
13837         if(!id){
13838             id = setInterval(call, d);
13839         }
13840     };
13841
13842     /**
13843      * Cancel the last queued timeout
13844      */
13845     this.cancel = function(){
13846         if(id){
13847             clearInterval(id);
13848             id = null;
13849         }
13850     };
13851 };/*
13852  * Based on:
13853  * Ext JS Library 1.1.1
13854  * Copyright(c) 2006-2007, Ext JS, LLC.
13855  *
13856  * Originally Released Under LGPL - original licence link has changed is not relivant.
13857  *
13858  * Fork - LGPL
13859  * <script type="text/javascript">
13860  */
13861 /**
13862  * @class Roo.util.TaskRunner
13863  * Manage background tasks - not sure why this is better that setInterval?
13864  * @static
13865  *
13866  */
13867  
13868 Roo.util.TaskRunner = function(interval){
13869     interval = interval || 10;
13870     var tasks = [], removeQueue = [];
13871     var id = 0;
13872     var running = false;
13873
13874     var stopThread = function(){
13875         running = false;
13876         clearInterval(id);
13877         id = 0;
13878     };
13879
13880     var startThread = function(){
13881         if(!running){
13882             running = true;
13883             id = setInterval(runTasks, interval);
13884         }
13885     };
13886
13887     var removeTask = function(task){
13888         removeQueue.push(task);
13889         if(task.onStop){
13890             task.onStop();
13891         }
13892     };
13893
13894     var runTasks = function(){
13895         if(removeQueue.length > 0){
13896             for(var i = 0, len = removeQueue.length; i < len; i++){
13897                 tasks.remove(removeQueue[i]);
13898             }
13899             removeQueue = [];
13900             if(tasks.length < 1){
13901                 stopThread();
13902                 return;
13903             }
13904         }
13905         var now = new Date().getTime();
13906         for(var i = 0, len = tasks.length; i < len; ++i){
13907             var t = tasks[i];
13908             var itime = now - t.taskRunTime;
13909             if(t.interval <= itime){
13910                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13911                 t.taskRunTime = now;
13912                 if(rt === false || t.taskRunCount === t.repeat){
13913                     removeTask(t);
13914                     return;
13915                 }
13916             }
13917             if(t.duration && t.duration <= (now - t.taskStartTime)){
13918                 removeTask(t);
13919             }
13920         }
13921     };
13922
13923     /**
13924      * Queues a new task.
13925      * @param {Object} task
13926      *
13927      * Task property : interval = how frequent to run.
13928      * Task object should implement
13929      * function run()
13930      * Task object may implement
13931      * function onStop()
13932      */
13933     this.start = function(task){
13934         tasks.push(task);
13935         task.taskStartTime = new Date().getTime();
13936         task.taskRunTime = 0;
13937         task.taskRunCount = 0;
13938         startThread();
13939         return task;
13940     };
13941     /**
13942      * Stop  new task.
13943      * @param {Object} task
13944      */
13945     this.stop = function(task){
13946         removeTask(task);
13947         return task;
13948     };
13949     /**
13950      * Stop all Tasks
13951      */
13952     this.stopAll = function(){
13953         stopThread();
13954         for(var i = 0, len = tasks.length; i < len; i++){
13955             if(tasks[i].onStop){
13956                 tasks[i].onStop();
13957             }
13958         }
13959         tasks = [];
13960         removeQueue = [];
13961     };
13962 };
13963
13964 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975  
13976 /**
13977  * @class Roo.util.MixedCollection
13978  * @extends Roo.util.Observable
13979  * A Collection class that maintains both numeric indexes and keys and exposes events.
13980  * @constructor
13981  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13982  * collection (defaults to false)
13983  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13984  * and return the key value for that item.  This is used when available to look up the key on items that
13985  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13986  * equivalent to providing an implementation for the {@link #getKey} method.
13987  */
13988 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13989     this.items = [];
13990     this.map = {};
13991     this.keys = [];
13992     this.length = 0;
13993     this.addEvents({
13994         /**
13995          * @event clear
13996          * Fires when the collection is cleared.
13997          */
13998         "clear" : true,
13999         /**
14000          * @event add
14001          * Fires when an item is added to the collection.
14002          * @param {Number} index The index at which the item was added.
14003          * @param {Object} o The item added.
14004          * @param {String} key The key associated with the added item.
14005          */
14006         "add" : true,
14007         /**
14008          * @event replace
14009          * Fires when an item is replaced in the collection.
14010          * @param {String} key he key associated with the new added.
14011          * @param {Object} old The item being replaced.
14012          * @param {Object} new The new item.
14013          */
14014         "replace" : true,
14015         /**
14016          * @event remove
14017          * Fires when an item is removed from the collection.
14018          * @param {Object} o The item being removed.
14019          * @param {String} key (optional) The key associated with the removed item.
14020          */
14021         "remove" : true,
14022         "sort" : true
14023     });
14024     this.allowFunctions = allowFunctions === true;
14025     if(keyFn){
14026         this.getKey = keyFn;
14027     }
14028     Roo.util.MixedCollection.superclass.constructor.call(this);
14029 };
14030
14031 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14032     allowFunctions : false,
14033     
14034 /**
14035  * Adds an item to the collection.
14036  * @param {String} key The key to associate with the item
14037  * @param {Object} o The item to add.
14038  * @return {Object} The item added.
14039  */
14040     add : function(key, o){
14041         if(arguments.length == 1){
14042             o = arguments[0];
14043             key = this.getKey(o);
14044         }
14045         if(typeof key == "undefined" || key === null){
14046             this.length++;
14047             this.items.push(o);
14048             this.keys.push(null);
14049         }else{
14050             var old = this.map[key];
14051             if(old){
14052                 return this.replace(key, o);
14053             }
14054             this.length++;
14055             this.items.push(o);
14056             this.map[key] = o;
14057             this.keys.push(key);
14058         }
14059         this.fireEvent("add", this.length-1, o, key);
14060         return o;
14061     },
14062        
14063 /**
14064   * MixedCollection has a generic way to fetch keys if you implement getKey.
14065 <pre><code>
14066 // normal way
14067 var mc = new Roo.util.MixedCollection();
14068 mc.add(someEl.dom.id, someEl);
14069 mc.add(otherEl.dom.id, otherEl);
14070 //and so on
14071
14072 // using getKey
14073 var mc = new Roo.util.MixedCollection();
14074 mc.getKey = function(el){
14075    return el.dom.id;
14076 };
14077 mc.add(someEl);
14078 mc.add(otherEl);
14079
14080 // or via the constructor
14081 var mc = new Roo.util.MixedCollection(false, function(el){
14082    return el.dom.id;
14083 });
14084 mc.add(someEl);
14085 mc.add(otherEl);
14086 </code></pre>
14087  * @param o {Object} The item for which to find the key.
14088  * @return {Object} The key for the passed item.
14089  */
14090     getKey : function(o){
14091          return o.id; 
14092     },
14093    
14094 /**
14095  * Replaces an item in the collection.
14096  * @param {String} key The key associated with the item to replace, or the item to replace.
14097  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14098  * @return {Object}  The new item.
14099  */
14100     replace : function(key, o){
14101         if(arguments.length == 1){
14102             o = arguments[0];
14103             key = this.getKey(o);
14104         }
14105         var old = this.item(key);
14106         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14107              return this.add(key, o);
14108         }
14109         var index = this.indexOfKey(key);
14110         this.items[index] = o;
14111         this.map[key] = o;
14112         this.fireEvent("replace", key, old, o);
14113         return o;
14114     },
14115    
14116 /**
14117  * Adds all elements of an Array or an Object to the collection.
14118  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14119  * an Array of values, each of which are added to the collection.
14120  */
14121     addAll : function(objs){
14122         if(arguments.length > 1 || objs instanceof Array){
14123             var args = arguments.length > 1 ? arguments : objs;
14124             for(var i = 0, len = args.length; i < len; i++){
14125                 this.add(args[i]);
14126             }
14127         }else{
14128             for(var key in objs){
14129                 if(this.allowFunctions || typeof objs[key] != "function"){
14130                     this.add(key, objs[key]);
14131                 }
14132             }
14133         }
14134     },
14135    
14136 /**
14137  * Executes the specified function once for every item in the collection, passing each
14138  * item as the first and only parameter. returning false from the function will stop the iteration.
14139  * @param {Function} fn The function to execute for each item.
14140  * @param {Object} scope (optional) The scope in which to execute the function.
14141  */
14142     each : function(fn, scope){
14143         var items = [].concat(this.items); // each safe for removal
14144         for(var i = 0, len = items.length; i < len; i++){
14145             if(fn.call(scope || items[i], items[i], i, len) === false){
14146                 break;
14147             }
14148         }
14149     },
14150    
14151 /**
14152  * Executes the specified function once for every key in the collection, passing each
14153  * key, and its associated item as the first two parameters.
14154  * @param {Function} fn The function to execute for each item.
14155  * @param {Object} scope (optional) The scope in which to execute the function.
14156  */
14157     eachKey : function(fn, scope){
14158         for(var i = 0, len = this.keys.length; i < len; i++){
14159             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14160         }
14161     },
14162    
14163 /**
14164  * Returns the first item in the collection which elicits a true return value from the
14165  * passed selection function.
14166  * @param {Function} fn The selection function to execute for each item.
14167  * @param {Object} scope (optional) The scope in which to execute the function.
14168  * @return {Object} The first item in the collection which returned true from the selection function.
14169  */
14170     find : function(fn, scope){
14171         for(var i = 0, len = this.items.length; i < len; i++){
14172             if(fn.call(scope || window, this.items[i], this.keys[i])){
14173                 return this.items[i];
14174             }
14175         }
14176         return null;
14177     },
14178    
14179 /**
14180  * Inserts an item at the specified index in the collection.
14181  * @param {Number} index The index to insert the item at.
14182  * @param {String} key The key to associate with the new item, or the item itself.
14183  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14184  * @return {Object} The item inserted.
14185  */
14186     insert : function(index, key, o){
14187         if(arguments.length == 2){
14188             o = arguments[1];
14189             key = this.getKey(o);
14190         }
14191         if(index >= this.length){
14192             return this.add(key, o);
14193         }
14194         this.length++;
14195         this.items.splice(index, 0, o);
14196         if(typeof key != "undefined" && key != null){
14197             this.map[key] = o;
14198         }
14199         this.keys.splice(index, 0, key);
14200         this.fireEvent("add", index, o, key);
14201         return o;
14202     },
14203    
14204 /**
14205  * Removed an item from the collection.
14206  * @param {Object} o The item to remove.
14207  * @return {Object} The item removed.
14208  */
14209     remove : function(o){
14210         return this.removeAt(this.indexOf(o));
14211     },
14212    
14213 /**
14214  * Remove an item from a specified index in the collection.
14215  * @param {Number} index The index within the collection of the item to remove.
14216  */
14217     removeAt : function(index){
14218         if(index < this.length && index >= 0){
14219             this.length--;
14220             var o = this.items[index];
14221             this.items.splice(index, 1);
14222             var key = this.keys[index];
14223             if(typeof key != "undefined"){
14224                 delete this.map[key];
14225             }
14226             this.keys.splice(index, 1);
14227             this.fireEvent("remove", o, key);
14228         }
14229     },
14230    
14231 /**
14232  * Removed an item associated with the passed key fom the collection.
14233  * @param {String} key The key of the item to remove.
14234  */
14235     removeKey : function(key){
14236         return this.removeAt(this.indexOfKey(key));
14237     },
14238    
14239 /**
14240  * Returns the number of items in the collection.
14241  * @return {Number} the number of items in the collection.
14242  */
14243     getCount : function(){
14244         return this.length; 
14245     },
14246    
14247 /**
14248  * Returns index within the collection of the passed Object.
14249  * @param {Object} o The item to find the index of.
14250  * @return {Number} index of the item.
14251  */
14252     indexOf : function(o){
14253         if(!this.items.indexOf){
14254             for(var i = 0, len = this.items.length; i < len; i++){
14255                 if(this.items[i] == o) {
14256                     return i;
14257                 }
14258             }
14259             return -1;
14260         }else{
14261             return this.items.indexOf(o);
14262         }
14263     },
14264    
14265 /**
14266  * Returns index within the collection of the passed key.
14267  * @param {String} key The key to find the index of.
14268  * @return {Number} index of the key.
14269  */
14270     indexOfKey : function(key){
14271         if(!this.keys.indexOf){
14272             for(var i = 0, len = this.keys.length; i < len; i++){
14273                 if(this.keys[i] == key) {
14274                     return i;
14275                 }
14276             }
14277             return -1;
14278         }else{
14279             return this.keys.indexOf(key);
14280         }
14281     },
14282    
14283 /**
14284  * Returns the item associated with the passed key OR index. Key has priority over index.
14285  * @param {String/Number} key The key or index of the item.
14286  * @return {Object} The item associated with the passed key.
14287  */
14288     item : function(key){
14289         if (key === 'length') {
14290             return null;
14291         }
14292         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14293         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14294     },
14295     
14296 /**
14297  * Returns the item at the specified index.
14298  * @param {Number} index The index of the item.
14299  * @return {Object}
14300  */
14301     itemAt : function(index){
14302         return this.items[index];
14303     },
14304     
14305 /**
14306  * Returns the item associated with the passed key.
14307  * @param {String/Number} key The key of the item.
14308  * @return {Object} The item associated with the passed key.
14309  */
14310     key : function(key){
14311         return this.map[key];
14312     },
14313    
14314 /**
14315  * Returns true if the collection contains the passed Object as an item.
14316  * @param {Object} o  The Object to look for in the collection.
14317  * @return {Boolean} True if the collection contains the Object as an item.
14318  */
14319     contains : function(o){
14320         return this.indexOf(o) != -1;
14321     },
14322    
14323 /**
14324  * Returns true if the collection contains the passed Object as a key.
14325  * @param {String} key The key to look for in the collection.
14326  * @return {Boolean} True if the collection contains the Object as a key.
14327  */
14328     containsKey : function(key){
14329         return typeof this.map[key] != "undefined";
14330     },
14331    
14332 /**
14333  * Removes all items from the collection.
14334  */
14335     clear : function(){
14336         this.length = 0;
14337         this.items = [];
14338         this.keys = [];
14339         this.map = {};
14340         this.fireEvent("clear");
14341     },
14342    
14343 /**
14344  * Returns the first item in the collection.
14345  * @return {Object} the first item in the collection..
14346  */
14347     first : function(){
14348         return this.items[0]; 
14349     },
14350    
14351 /**
14352  * Returns the last item in the collection.
14353  * @return {Object} the last item in the collection..
14354  */
14355     last : function(){
14356         return this.items[this.length-1];   
14357     },
14358     
14359     _sort : function(property, dir, fn){
14360         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14361         fn = fn || function(a, b){
14362             return a-b;
14363         };
14364         var c = [], k = this.keys, items = this.items;
14365         for(var i = 0, len = items.length; i < len; i++){
14366             c[c.length] = {key: k[i], value: items[i], index: i};
14367         }
14368         c.sort(function(a, b){
14369             var v = fn(a[property], b[property]) * dsc;
14370             if(v == 0){
14371                 v = (a.index < b.index ? -1 : 1);
14372             }
14373             return v;
14374         });
14375         for(var i = 0, len = c.length; i < len; i++){
14376             items[i] = c[i].value;
14377             k[i] = c[i].key;
14378         }
14379         this.fireEvent("sort", this);
14380     },
14381     
14382     /**
14383      * Sorts this collection with the passed comparison function
14384      * @param {String} direction (optional) "ASC" or "DESC"
14385      * @param {Function} fn (optional) comparison function
14386      */
14387     sort : function(dir, fn){
14388         this._sort("value", dir, fn);
14389     },
14390     
14391     /**
14392      * Sorts this collection by keys
14393      * @param {String} direction (optional) "ASC" or "DESC"
14394      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14395      */
14396     keySort : function(dir, fn){
14397         this._sort("key", dir, fn || function(a, b){
14398             return String(a).toUpperCase()-String(b).toUpperCase();
14399         });
14400     },
14401     
14402     /**
14403      * Returns a range of items in this collection
14404      * @param {Number} startIndex (optional) defaults to 0
14405      * @param {Number} endIndex (optional) default to the last item
14406      * @return {Array} An array of items
14407      */
14408     getRange : function(start, end){
14409         var items = this.items;
14410         if(items.length < 1){
14411             return [];
14412         }
14413         start = start || 0;
14414         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14415         var r = [];
14416         if(start <= end){
14417             for(var i = start; i <= end; i++) {
14418                     r[r.length] = items[i];
14419             }
14420         }else{
14421             for(var i = start; i >= end; i--) {
14422                     r[r.length] = items[i];
14423             }
14424         }
14425         return r;
14426     },
14427         
14428     /**
14429      * Filter the <i>objects</i> in this collection by a specific property. 
14430      * Returns a new collection that has been filtered.
14431      * @param {String} property A property on your objects
14432      * @param {String/RegExp} value Either string that the property values 
14433      * should start with or a RegExp to test against the property
14434      * @return {MixedCollection} The new filtered collection
14435      */
14436     filter : function(property, value){
14437         if(!value.exec){ // not a regex
14438             value = String(value);
14439             if(value.length == 0){
14440                 return this.clone();
14441             }
14442             value = new RegExp("^" + Roo.escapeRe(value), "i");
14443         }
14444         return this.filterBy(function(o){
14445             return o && value.test(o[property]);
14446         });
14447         },
14448     
14449     /**
14450      * Filter by a function. * Returns a new collection that has been filtered.
14451      * The passed function will be called with each 
14452      * object in the collection. If the function returns true, the value is included 
14453      * otherwise it is filtered.
14454      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14455      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14456      * @return {MixedCollection} The new filtered collection
14457      */
14458     filterBy : function(fn, scope){
14459         var r = new Roo.util.MixedCollection();
14460         r.getKey = this.getKey;
14461         var k = this.keys, it = this.items;
14462         for(var i = 0, len = it.length; i < len; i++){
14463             if(fn.call(scope||this, it[i], k[i])){
14464                                 r.add(k[i], it[i]);
14465                         }
14466         }
14467         return r;
14468     },
14469     
14470     /**
14471      * Creates a duplicate of this collection
14472      * @return {MixedCollection}
14473      */
14474     clone : function(){
14475         var r = new Roo.util.MixedCollection();
14476         var k = this.keys, it = this.items;
14477         for(var i = 0, len = it.length; i < len; i++){
14478             r.add(k[i], it[i]);
14479         }
14480         r.getKey = this.getKey;
14481         return r;
14482     }
14483 });
14484 /**
14485  * Returns the item associated with the passed key or index.
14486  * @method
14487  * @param {String/Number} key The key or index of the item.
14488  * @return {Object} The item associated with the passed key.
14489  */
14490 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14491  * Based on:
14492  * Ext JS Library 1.1.1
14493  * Copyright(c) 2006-2007, Ext JS, LLC.
14494  *
14495  * Originally Released Under LGPL - original licence link has changed is not relivant.
14496  *
14497  * Fork - LGPL
14498  * <script type="text/javascript">
14499  */
14500 /**
14501  * @class Roo.util.JSON
14502  * Modified version of Douglas Crockford"s json.js that doesn"t
14503  * mess with the Object prototype 
14504  * http://www.json.org/js.html
14505  * @static
14506  */
14507 Roo.util.JSON = new (function(){
14508     var useHasOwn = {}.hasOwnProperty ? true : false;
14509     
14510     // crashes Safari in some instances
14511     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14512     
14513     var pad = function(n) {
14514         return n < 10 ? "0" + n : n;
14515     };
14516     
14517     var m = {
14518         "\b": '\\b',
14519         "\t": '\\t',
14520         "\n": '\\n',
14521         "\f": '\\f',
14522         "\r": '\\r',
14523         '"' : '\\"',
14524         "\\": '\\\\'
14525     };
14526
14527     var encodeString = function(s){
14528         if (/["\\\x00-\x1f]/.test(s)) {
14529             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14530                 var c = m[b];
14531                 if(c){
14532                     return c;
14533                 }
14534                 c = b.charCodeAt();
14535                 return "\\u00" +
14536                     Math.floor(c / 16).toString(16) +
14537                     (c % 16).toString(16);
14538             }) + '"';
14539         }
14540         return '"' + s + '"';
14541     };
14542     
14543     var encodeArray = function(o){
14544         var a = ["["], b, i, l = o.length, v;
14545             for (i = 0; i < l; i += 1) {
14546                 v = o[i];
14547                 switch (typeof v) {
14548                     case "undefined":
14549                     case "function":
14550                     case "unknown":
14551                         break;
14552                     default:
14553                         if (b) {
14554                             a.push(',');
14555                         }
14556                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14557                         b = true;
14558                 }
14559             }
14560             a.push("]");
14561             return a.join("");
14562     };
14563     
14564     var encodeDate = function(o){
14565         return '"' + o.getFullYear() + "-" +
14566                 pad(o.getMonth() + 1) + "-" +
14567                 pad(o.getDate()) + "T" +
14568                 pad(o.getHours()) + ":" +
14569                 pad(o.getMinutes()) + ":" +
14570                 pad(o.getSeconds()) + '"';
14571     };
14572     
14573     /**
14574      * Encodes an Object, Array or other value
14575      * @param {Mixed} o The variable to encode
14576      * @return {String} The JSON string
14577      */
14578     this.encode = function(o)
14579     {
14580         // should this be extended to fully wrap stringify..
14581         
14582         if(typeof o == "undefined" || o === null){
14583             return "null";
14584         }else if(o instanceof Array){
14585             return encodeArray(o);
14586         }else if(o instanceof Date){
14587             return encodeDate(o);
14588         }else if(typeof o == "string"){
14589             return encodeString(o);
14590         }else if(typeof o == "number"){
14591             return isFinite(o) ? String(o) : "null";
14592         }else if(typeof o == "boolean"){
14593             return String(o);
14594         }else {
14595             var a = ["{"], b, i, v;
14596             for (i in o) {
14597                 if(!useHasOwn || o.hasOwnProperty(i)) {
14598                     v = o[i];
14599                     switch (typeof v) {
14600                     case "undefined":
14601                     case "function":
14602                     case "unknown":
14603                         break;
14604                     default:
14605                         if(b){
14606                             a.push(',');
14607                         }
14608                         a.push(this.encode(i), ":",
14609                                 v === null ? "null" : this.encode(v));
14610                         b = true;
14611                     }
14612                 }
14613             }
14614             a.push("}");
14615             return a.join("");
14616         }
14617     };
14618     
14619     /**
14620      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14621      * @param {String} json The JSON string
14622      * @return {Object} The resulting object
14623      */
14624     this.decode = function(json){
14625         
14626         return  /** eval:var:json */ eval("(" + json + ')');
14627     };
14628 })();
14629 /** 
14630  * Shorthand for {@link Roo.util.JSON#encode}
14631  * @member Roo encode 
14632  * @method */
14633 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14634 /** 
14635  * Shorthand for {@link Roo.util.JSON#decode}
14636  * @member Roo decode 
14637  * @method */
14638 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14639 /*
14640  * Based on:
14641  * Ext JS Library 1.1.1
14642  * Copyright(c) 2006-2007, Ext JS, LLC.
14643  *
14644  * Originally Released Under LGPL - original licence link has changed is not relivant.
14645  *
14646  * Fork - LGPL
14647  * <script type="text/javascript">
14648  */
14649  
14650 /**
14651  * @class Roo.util.Format
14652  * Reusable data formatting functions
14653  * @static
14654  */
14655 Roo.util.Format = function(){
14656     var trimRe = /^\s+|\s+$/g;
14657     return {
14658         /**
14659          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14660          * @param {String} value The string to truncate
14661          * @param {Number} length The maximum length to allow before truncating
14662          * @return {String} The converted text
14663          */
14664         ellipsis : function(value, len){
14665             if(value && value.length > len){
14666                 return value.substr(0, len-3)+"...";
14667             }
14668             return value;
14669         },
14670
14671         /**
14672          * Checks a reference and converts it to empty string if it is undefined
14673          * @param {Mixed} value Reference to check
14674          * @return {Mixed} Empty string if converted, otherwise the original value
14675          */
14676         undef : function(value){
14677             return typeof value != "undefined" ? value : "";
14678         },
14679
14680         /**
14681          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14682          * @param {String} value The string to encode
14683          * @return {String} The encoded text
14684          */
14685         htmlEncode : function(value){
14686             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14687         },
14688
14689         /**
14690          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14691          * @param {String} value The string to decode
14692          * @return {String} The decoded text
14693          */
14694         htmlDecode : function(value){
14695             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14696         },
14697
14698         /**
14699          * Trims any whitespace from either side of a string
14700          * @param {String} value The text to trim
14701          * @return {String} The trimmed text
14702          */
14703         trim : function(value){
14704             return String(value).replace(trimRe, "");
14705         },
14706
14707         /**
14708          * Returns a substring from within an original string
14709          * @param {String} value The original text
14710          * @param {Number} start The start index of the substring
14711          * @param {Number} length The length of the substring
14712          * @return {String} The substring
14713          */
14714         substr : function(value, start, length){
14715             return String(value).substr(start, length);
14716         },
14717
14718         /**
14719          * Converts a string to all lower case letters
14720          * @param {String} value The text to convert
14721          * @return {String} The converted text
14722          */
14723         lowercase : function(value){
14724             return String(value).toLowerCase();
14725         },
14726
14727         /**
14728          * Converts a string to all upper case letters
14729          * @param {String} value The text to convert
14730          * @return {String} The converted text
14731          */
14732         uppercase : function(value){
14733             return String(value).toUpperCase();
14734         },
14735
14736         /**
14737          * Converts the first character only of a string to upper case
14738          * @param {String} value The text to convert
14739          * @return {String} The converted text
14740          */
14741         capitalize : function(value){
14742             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14743         },
14744
14745         // private
14746         call : function(value, fn){
14747             if(arguments.length > 2){
14748                 var args = Array.prototype.slice.call(arguments, 2);
14749                 args.unshift(value);
14750                  
14751                 return /** eval:var:value */  eval(fn).apply(window, args);
14752             }else{
14753                 /** eval:var:value */
14754                 return /** eval:var:value */ eval(fn).call(window, value);
14755             }
14756         },
14757
14758        
14759         /**
14760          * safer version of Math.toFixed..??/
14761          * @param {Number/String} value The numeric value to format
14762          * @param {Number/String} value Decimal places 
14763          * @return {String} The formatted currency string
14764          */
14765         toFixed : function(v, n)
14766         {
14767             // why not use to fixed - precision is buggered???
14768             if (!n) {
14769                 return Math.round(v-0);
14770             }
14771             var fact = Math.pow(10,n+1);
14772             v = (Math.round((v-0)*fact))/fact;
14773             var z = (''+fact).substring(2);
14774             if (v == Math.floor(v)) {
14775                 return Math.floor(v) + '.' + z;
14776             }
14777             
14778             // now just padd decimals..
14779             var ps = String(v).split('.');
14780             var fd = (ps[1] + z);
14781             var r = fd.substring(0,n); 
14782             var rm = fd.substring(n); 
14783             if (rm < 5) {
14784                 return ps[0] + '.' + r;
14785             }
14786             r*=1; // turn it into a number;
14787             r++;
14788             if (String(r).length != n) {
14789                 ps[0]*=1;
14790                 ps[0]++;
14791                 r = String(r).substring(1); // chop the end off.
14792             }
14793             
14794             return ps[0] + '.' + r;
14795              
14796         },
14797         
14798         /**
14799          * Format a number as US currency
14800          * @param {Number/String} value The numeric value to format
14801          * @return {String} The formatted currency string
14802          */
14803         usMoney : function(v){
14804             return '$' + Roo.util.Format.number(v);
14805         },
14806         
14807         /**
14808          * Format a number
14809          * eventually this should probably emulate php's number_format
14810          * @param {Number/String} value The numeric value to format
14811          * @param {Number} decimals number of decimal places
14812          * @param {String} delimiter for thousands (default comma)
14813          * @return {String} The formatted currency string
14814          */
14815         number : function(v, decimals, thousandsDelimiter)
14816         {
14817             // multiply and round.
14818             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14819             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14820             
14821             var mul = Math.pow(10, decimals);
14822             var zero = String(mul).substring(1);
14823             v = (Math.round((v-0)*mul))/mul;
14824             
14825             // if it's '0' number.. then
14826             
14827             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14828             v = String(v);
14829             var ps = v.split('.');
14830             var whole = ps[0];
14831             
14832             var r = /(\d+)(\d{3})/;
14833             // add comma's
14834             
14835             if(thousandsDelimiter.length != 0) {
14836                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14837             } 
14838             
14839             var sub = ps[1] ?
14840                     // has decimals..
14841                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14842                     // does not have decimals
14843                     (decimals ? ('.' + zero) : '');
14844             
14845             
14846             return whole + sub ;
14847         },
14848         
14849         /**
14850          * Parse a value into a formatted date using the specified format pattern.
14851          * @param {Mixed} value The value to format
14852          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14853          * @return {String} The formatted date string
14854          */
14855         date : function(v, format){
14856             if(!v){
14857                 return "";
14858             }
14859             if(!(v instanceof Date)){
14860                 v = new Date(Date.parse(v));
14861             }
14862             return v.dateFormat(format || Roo.util.Format.defaults.date);
14863         },
14864
14865         /**
14866          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14867          * @param {String} format Any valid date format string
14868          * @return {Function} The date formatting function
14869          */
14870         dateRenderer : function(format){
14871             return function(v){
14872                 return Roo.util.Format.date(v, format);  
14873             };
14874         },
14875
14876         // private
14877         stripTagsRE : /<\/?[^>]+>/gi,
14878         
14879         /**
14880          * Strips all HTML tags
14881          * @param {Mixed} value The text from which to strip tags
14882          * @return {String} The stripped text
14883          */
14884         stripTags : function(v){
14885             return !v ? v : String(v).replace(this.stripTagsRE, "");
14886         },
14887         
14888         /**
14889          * Size in Mb,Gb etc.
14890          * @param {Number} value The number to be formated
14891          * @param {number} decimals how many decimal places
14892          * @return {String} the formated string
14893          */
14894         size : function(value, decimals)
14895         {
14896             var sizes = ['b', 'k', 'M', 'G', 'T'];
14897             if (value == 0) {
14898                 return 0;
14899             }
14900             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14901             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14902         }
14903         
14904         
14905         
14906     };
14907 }();
14908 Roo.util.Format.defaults = {
14909     date : 'd/M/Y'
14910 };/*
14911  * Based on:
14912  * Ext JS Library 1.1.1
14913  * Copyright(c) 2006-2007, Ext JS, LLC.
14914  *
14915  * Originally Released Under LGPL - original licence link has changed is not relivant.
14916  *
14917  * Fork - LGPL
14918  * <script type="text/javascript">
14919  */
14920
14921
14922  
14923
14924 /**
14925  * @class Roo.MasterTemplate
14926  * @extends Roo.Template
14927  * Provides a template that can have child templates. The syntax is:
14928 <pre><code>
14929 var t = new Roo.MasterTemplate(
14930         '&lt;select name="{name}"&gt;',
14931                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14932         '&lt;/select&gt;'
14933 );
14934 t.add('options', {value: 'foo', text: 'bar'});
14935 // or you can add multiple child elements in one shot
14936 t.addAll('options', [
14937     {value: 'foo', text: 'bar'},
14938     {value: 'foo2', text: 'bar2'},
14939     {value: 'foo3', text: 'bar3'}
14940 ]);
14941 // then append, applying the master template values
14942 t.append('my-form', {name: 'my-select'});
14943 </code></pre>
14944 * A name attribute for the child template is not required if you have only one child
14945 * template or you want to refer to them by index.
14946  */
14947 Roo.MasterTemplate = function(){
14948     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14949     this.originalHtml = this.html;
14950     var st = {};
14951     var m, re = this.subTemplateRe;
14952     re.lastIndex = 0;
14953     var subIndex = 0;
14954     while(m = re.exec(this.html)){
14955         var name = m[1], content = m[2];
14956         st[subIndex] = {
14957             name: name,
14958             index: subIndex,
14959             buffer: [],
14960             tpl : new Roo.Template(content)
14961         };
14962         if(name){
14963             st[name] = st[subIndex];
14964         }
14965         st[subIndex].tpl.compile();
14966         st[subIndex].tpl.call = this.call.createDelegate(this);
14967         subIndex++;
14968     }
14969     this.subCount = subIndex;
14970     this.subs = st;
14971 };
14972 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14973     /**
14974     * The regular expression used to match sub templates
14975     * @type RegExp
14976     * @property
14977     */
14978     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14979
14980     /**
14981      * Applies the passed values to a child template.
14982      * @param {String/Number} name (optional) The name or index of the child template
14983      * @param {Array/Object} values The values to be applied to the template
14984      * @return {MasterTemplate} this
14985      */
14986      add : function(name, values){
14987         if(arguments.length == 1){
14988             values = arguments[0];
14989             name = 0;
14990         }
14991         var s = this.subs[name];
14992         s.buffer[s.buffer.length] = s.tpl.apply(values);
14993         return this;
14994     },
14995
14996     /**
14997      * Applies all the passed values to a child template.
14998      * @param {String/Number} name (optional) The name or index of the child template
14999      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15000      * @param {Boolean} reset (optional) True to reset the template first
15001      * @return {MasterTemplate} this
15002      */
15003     fill : function(name, values, reset){
15004         var a = arguments;
15005         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15006             values = a[0];
15007             name = 0;
15008             reset = a[1];
15009         }
15010         if(reset){
15011             this.reset();
15012         }
15013         for(var i = 0, len = values.length; i < len; i++){
15014             this.add(name, values[i]);
15015         }
15016         return this;
15017     },
15018
15019     /**
15020      * Resets the template for reuse
15021      * @return {MasterTemplate} this
15022      */
15023      reset : function(){
15024         var s = this.subs;
15025         for(var i = 0; i < this.subCount; i++){
15026             s[i].buffer = [];
15027         }
15028         return this;
15029     },
15030
15031     applyTemplate : function(values){
15032         var s = this.subs;
15033         var replaceIndex = -1;
15034         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15035             return s[++replaceIndex].buffer.join("");
15036         });
15037         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15038     },
15039
15040     apply : function(){
15041         return this.applyTemplate.apply(this, arguments);
15042     },
15043
15044     compile : function(){return this;}
15045 });
15046
15047 /**
15048  * Alias for fill().
15049  * @method
15050  */
15051 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15052  /**
15053  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15054  * var tpl = Roo.MasterTemplate.from('element-id');
15055  * @param {String/HTMLElement} el
15056  * @param {Object} config
15057  * @static
15058  */
15059 Roo.MasterTemplate.from = function(el, config){
15060     el = Roo.getDom(el);
15061     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15062 };/*
15063  * Based on:
15064  * Ext JS Library 1.1.1
15065  * Copyright(c) 2006-2007, Ext JS, LLC.
15066  *
15067  * Originally Released Under LGPL - original licence link has changed is not relivant.
15068  *
15069  * Fork - LGPL
15070  * <script type="text/javascript">
15071  */
15072
15073  
15074 /**
15075  * @class Roo.util.CSS
15076  * Utility class for manipulating CSS rules
15077  * @static
15078
15079  */
15080 Roo.util.CSS = function(){
15081         var rules = null;
15082         var doc = document;
15083
15084     var camelRe = /(-[a-z])/gi;
15085     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15086
15087    return {
15088    /**
15089     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15090     * tag and appended to the HEAD of the document.
15091     * @param {String|Object} cssText The text containing the css rules
15092     * @param {String} id An id to add to the stylesheet for later removal
15093     * @return {StyleSheet}
15094     */
15095     createStyleSheet : function(cssText, id){
15096         var ss;
15097         var head = doc.getElementsByTagName("head")[0];
15098         var nrules = doc.createElement("style");
15099         nrules.setAttribute("type", "text/css");
15100         if(id){
15101             nrules.setAttribute("id", id);
15102         }
15103         if (typeof(cssText) != 'string') {
15104             // support object maps..
15105             // not sure if this a good idea.. 
15106             // perhaps it should be merged with the general css handling
15107             // and handle js style props.
15108             var cssTextNew = [];
15109             for(var n in cssText) {
15110                 var citems = [];
15111                 for(var k in cssText[n]) {
15112                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15113                 }
15114                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15115                 
15116             }
15117             cssText = cssTextNew.join("\n");
15118             
15119         }
15120        
15121        
15122        if(Roo.isIE){
15123            head.appendChild(nrules);
15124            ss = nrules.styleSheet;
15125            ss.cssText = cssText;
15126        }else{
15127            try{
15128                 nrules.appendChild(doc.createTextNode(cssText));
15129            }catch(e){
15130                nrules.cssText = cssText; 
15131            }
15132            head.appendChild(nrules);
15133            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15134        }
15135        this.cacheStyleSheet(ss);
15136        return ss;
15137    },
15138
15139    /**
15140     * Removes a style or link tag by id
15141     * @param {String} id The id of the tag
15142     */
15143    removeStyleSheet : function(id){
15144        var existing = doc.getElementById(id);
15145        if(existing){
15146            existing.parentNode.removeChild(existing);
15147        }
15148    },
15149
15150    /**
15151     * Dynamically swaps an existing stylesheet reference for a new one
15152     * @param {String} id The id of an existing link tag to remove
15153     * @param {String} url The href of the new stylesheet to include
15154     */
15155    swapStyleSheet : function(id, url){
15156        this.removeStyleSheet(id);
15157        var ss = doc.createElement("link");
15158        ss.setAttribute("rel", "stylesheet");
15159        ss.setAttribute("type", "text/css");
15160        ss.setAttribute("id", id);
15161        ss.setAttribute("href", url);
15162        doc.getElementsByTagName("head")[0].appendChild(ss);
15163    },
15164    
15165    /**
15166     * Refresh the rule cache if you have dynamically added stylesheets
15167     * @return {Object} An object (hash) of rules indexed by selector
15168     */
15169    refreshCache : function(){
15170        return this.getRules(true);
15171    },
15172
15173    // private
15174    cacheStyleSheet : function(stylesheet){
15175        if(!rules){
15176            rules = {};
15177        }
15178        try{// try catch for cross domain access issue
15179            var ssRules = stylesheet.cssRules || stylesheet.rules;
15180            for(var j = ssRules.length-1; j >= 0; --j){
15181                rules[ssRules[j].selectorText] = ssRules[j];
15182            }
15183        }catch(e){}
15184    },
15185    
15186    /**
15187     * Gets all css rules for the document
15188     * @param {Boolean} refreshCache true to refresh the internal cache
15189     * @return {Object} An object (hash) of rules indexed by selector
15190     */
15191    getRules : function(refreshCache){
15192                 if(rules == null || refreshCache){
15193                         rules = {};
15194                         var ds = doc.styleSheets;
15195                         for(var i =0, len = ds.length; i < len; i++){
15196                             try{
15197                         this.cacheStyleSheet(ds[i]);
15198                     }catch(e){} 
15199                 }
15200                 }
15201                 return rules;
15202         },
15203         
15204         /**
15205     * Gets an an individual CSS rule by selector(s)
15206     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15207     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15208     * @return {CSSRule} The CSS rule or null if one is not found
15209     */
15210    getRule : function(selector, refreshCache){
15211                 var rs = this.getRules(refreshCache);
15212                 if(!(selector instanceof Array)){
15213                     return rs[selector];
15214                 }
15215                 for(var i = 0; i < selector.length; i++){
15216                         if(rs[selector[i]]){
15217                                 return rs[selector[i]];
15218                         }
15219                 }
15220                 return null;
15221         },
15222         
15223         
15224         /**
15225     * Updates a rule property
15226     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15227     * @param {String} property The css property
15228     * @param {String} value The new value for the property
15229     * @return {Boolean} true If a rule was found and updated
15230     */
15231    updateRule : function(selector, property, value){
15232                 if(!(selector instanceof Array)){
15233                         var rule = this.getRule(selector);
15234                         if(rule){
15235                                 rule.style[property.replace(camelRe, camelFn)] = value;
15236                                 return true;
15237                         }
15238                 }else{
15239                         for(var i = 0; i < selector.length; i++){
15240                                 if(this.updateRule(selector[i], property, value)){
15241                                         return true;
15242                                 }
15243                         }
15244                 }
15245                 return false;
15246         }
15247    };   
15248 }();/*
15249  * Based on:
15250  * Ext JS Library 1.1.1
15251  * Copyright(c) 2006-2007, Ext JS, LLC.
15252  *
15253  * Originally Released Under LGPL - original licence link has changed is not relivant.
15254  *
15255  * Fork - LGPL
15256  * <script type="text/javascript">
15257  */
15258
15259  
15260
15261 /**
15262  * @class Roo.util.ClickRepeater
15263  * @extends Roo.util.Observable
15264  * 
15265  * A wrapper class which can be applied to any element. Fires a "click" event while the
15266  * mouse is pressed. The interval between firings may be specified in the config but
15267  * defaults to 10 milliseconds.
15268  * 
15269  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15270  * 
15271  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15272  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15273  * Similar to an autorepeat key delay.
15274  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15275  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15276  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15277  *           "interval" and "delay" are ignored. "immediate" is honored.
15278  * @cfg {Boolean} preventDefault True to prevent the default click event
15279  * @cfg {Boolean} stopDefault True to stop the default click event
15280  * 
15281  * @history
15282  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15283  *     2007-02-02 jvs Renamed to ClickRepeater
15284  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15285  *
15286  *  @constructor
15287  * @param {String/HTMLElement/Element} el The element to listen on
15288  * @param {Object} config
15289  **/
15290 Roo.util.ClickRepeater = function(el, config)
15291 {
15292     this.el = Roo.get(el);
15293     this.el.unselectable();
15294
15295     Roo.apply(this, config);
15296
15297     this.addEvents({
15298     /**
15299      * @event mousedown
15300      * Fires when the mouse button is depressed.
15301      * @param {Roo.util.ClickRepeater} this
15302      */
15303         "mousedown" : true,
15304     /**
15305      * @event click
15306      * Fires on a specified interval during the time the element is pressed.
15307      * @param {Roo.util.ClickRepeater} this
15308      */
15309         "click" : true,
15310     /**
15311      * @event mouseup
15312      * Fires when the mouse key is released.
15313      * @param {Roo.util.ClickRepeater} this
15314      */
15315         "mouseup" : true
15316     });
15317
15318     this.el.on("mousedown", this.handleMouseDown, this);
15319     if(this.preventDefault || this.stopDefault){
15320         this.el.on("click", function(e){
15321             if(this.preventDefault){
15322                 e.preventDefault();
15323             }
15324             if(this.stopDefault){
15325                 e.stopEvent();
15326             }
15327         }, this);
15328     }
15329
15330     // allow inline handler
15331     if(this.handler){
15332         this.on("click", this.handler,  this.scope || this);
15333     }
15334
15335     Roo.util.ClickRepeater.superclass.constructor.call(this);
15336 };
15337
15338 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15339     interval : 20,
15340     delay: 250,
15341     preventDefault : true,
15342     stopDefault : false,
15343     timer : 0,
15344
15345     // private
15346     handleMouseDown : function(){
15347         clearTimeout(this.timer);
15348         this.el.blur();
15349         if(this.pressClass){
15350             this.el.addClass(this.pressClass);
15351         }
15352         this.mousedownTime = new Date();
15353
15354         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15355         this.el.on("mouseout", this.handleMouseOut, this);
15356
15357         this.fireEvent("mousedown", this);
15358         this.fireEvent("click", this);
15359         
15360         this.timer = this.click.defer(this.delay || this.interval, this);
15361     },
15362
15363     // private
15364     click : function(){
15365         this.fireEvent("click", this);
15366         this.timer = this.click.defer(this.getInterval(), this);
15367     },
15368
15369     // private
15370     getInterval: function(){
15371         if(!this.accelerate){
15372             return this.interval;
15373         }
15374         var pressTime = this.mousedownTime.getElapsed();
15375         if(pressTime < 500){
15376             return 400;
15377         }else if(pressTime < 1700){
15378             return 320;
15379         }else if(pressTime < 2600){
15380             return 250;
15381         }else if(pressTime < 3500){
15382             return 180;
15383         }else if(pressTime < 4400){
15384             return 140;
15385         }else if(pressTime < 5300){
15386             return 80;
15387         }else if(pressTime < 6200){
15388             return 50;
15389         }else{
15390             return 10;
15391         }
15392     },
15393
15394     // private
15395     handleMouseOut : function(){
15396         clearTimeout(this.timer);
15397         if(this.pressClass){
15398             this.el.removeClass(this.pressClass);
15399         }
15400         this.el.on("mouseover", this.handleMouseReturn, this);
15401     },
15402
15403     // private
15404     handleMouseReturn : function(){
15405         this.el.un("mouseover", this.handleMouseReturn);
15406         if(this.pressClass){
15407             this.el.addClass(this.pressClass);
15408         }
15409         this.click();
15410     },
15411
15412     // private
15413     handleMouseUp : function(){
15414         clearTimeout(this.timer);
15415         this.el.un("mouseover", this.handleMouseReturn);
15416         this.el.un("mouseout", this.handleMouseOut);
15417         Roo.get(document).un("mouseup", this.handleMouseUp);
15418         this.el.removeClass(this.pressClass);
15419         this.fireEvent("mouseup", this);
15420     }
15421 });/**
15422  * @class Roo.util.Clipboard
15423  * @static
15424  * 
15425  * Clipboard UTILS
15426  * 
15427  **/
15428 Roo.util.Clipboard = {
15429     /**
15430      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15431      * @param {String} text to copy to clipboard
15432      */
15433     write : function(text) {
15434         // navigator clipboard api needs a secure context (https)
15435         if (navigator.clipboard && window.isSecureContext) {
15436             // navigator clipboard api method'
15437             navigator.clipboard.writeText(text);
15438             return ;
15439         } 
15440         // text area method
15441         var ta = document.createElement("textarea");
15442         ta.value = text;
15443         // make the textarea out of viewport
15444         ta.style.position = "fixed";
15445         ta.style.left = "-999999px";
15446         ta.style.top = "-999999px";
15447         document.body.appendChild(ta);
15448         ta.focus();
15449         ta.select();
15450         document.execCommand('copy');
15451         (function() {
15452             ta.remove();
15453         }).defer(100);
15454         
15455     }
15456         
15457 }
15458     /*
15459  * Based on:
15460  * Ext JS Library 1.1.1
15461  * Copyright(c) 2006-2007, Ext JS, LLC.
15462  *
15463  * Originally Released Under LGPL - original licence link has changed is not relivant.
15464  *
15465  * Fork - LGPL
15466  * <script type="text/javascript">
15467  */
15468
15469  
15470 /**
15471  * @class Roo.KeyNav
15472  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15473  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15474  * way to implement custom navigation schemes for any UI component.</p>
15475  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15476  * pageUp, pageDown, del, home, end.  Usage:</p>
15477  <pre><code>
15478 var nav = new Roo.KeyNav("my-element", {
15479     "left" : function(e){
15480         this.moveLeft(e.ctrlKey);
15481     },
15482     "right" : function(e){
15483         this.moveRight(e.ctrlKey);
15484     },
15485     "enter" : function(e){
15486         this.save();
15487     },
15488     scope : this
15489 });
15490 </code></pre>
15491  * @constructor
15492  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15493  * @param {Object} config The config
15494  */
15495 Roo.KeyNav = function(el, config){
15496     this.el = Roo.get(el);
15497     Roo.apply(this, config);
15498     if(!this.disabled){
15499         this.disabled = true;
15500         this.enable();
15501     }
15502 };
15503
15504 Roo.KeyNav.prototype = {
15505     /**
15506      * @cfg {Boolean} disabled
15507      * True to disable this KeyNav instance (defaults to false)
15508      */
15509     disabled : false,
15510     /**
15511      * @cfg {String} defaultEventAction
15512      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15513      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15514      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15515      */
15516     defaultEventAction: "stopEvent",
15517     /**
15518      * @cfg {Boolean} forceKeyDown
15519      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15520      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15521      * handle keydown instead of keypress.
15522      */
15523     forceKeyDown : false,
15524
15525     // private
15526     prepareEvent : function(e){
15527         var k = e.getKey();
15528         var h = this.keyToHandler[k];
15529         //if(h && this[h]){
15530         //    e.stopPropagation();
15531         //}
15532         if(Roo.isSafari && h && k >= 37 && k <= 40){
15533             e.stopEvent();
15534         }
15535     },
15536
15537     // private
15538     relay : function(e){
15539         var k = e.getKey();
15540         var h = this.keyToHandler[k];
15541         if(h && this[h]){
15542             if(this.doRelay(e, this[h], h) !== true){
15543                 e[this.defaultEventAction]();
15544             }
15545         }
15546     },
15547
15548     // private
15549     doRelay : function(e, h, hname){
15550         return h.call(this.scope || this, e);
15551     },
15552
15553     // possible handlers
15554     enter : false,
15555     left : false,
15556     right : false,
15557     up : false,
15558     down : false,
15559     tab : false,
15560     esc : false,
15561     pageUp : false,
15562     pageDown : false,
15563     del : false,
15564     home : false,
15565     end : false,
15566
15567     // quick lookup hash
15568     keyToHandler : {
15569         37 : "left",
15570         39 : "right",
15571         38 : "up",
15572         40 : "down",
15573         33 : "pageUp",
15574         34 : "pageDown",
15575         46 : "del",
15576         36 : "home",
15577         35 : "end",
15578         13 : "enter",
15579         27 : "esc",
15580         9  : "tab"
15581     },
15582
15583         /**
15584          * Enable this KeyNav
15585          */
15586         enable: function(){
15587                 if(this.disabled){
15588             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15589             // the EventObject will normalize Safari automatically
15590             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15591                 this.el.on("keydown", this.relay,  this);
15592             }else{
15593                 this.el.on("keydown", this.prepareEvent,  this);
15594                 this.el.on("keypress", this.relay,  this);
15595             }
15596                     this.disabled = false;
15597                 }
15598         },
15599
15600         /**
15601          * Disable this KeyNav
15602          */
15603         disable: function(){
15604                 if(!this.disabled){
15605                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15606                 this.el.un("keydown", this.relay);
15607             }else{
15608                 this.el.un("keydown", this.prepareEvent);
15609                 this.el.un("keypress", this.relay);
15610             }
15611                     this.disabled = true;
15612                 }
15613         }
15614 };/*
15615  * Based on:
15616  * Ext JS Library 1.1.1
15617  * Copyright(c) 2006-2007, Ext JS, LLC.
15618  *
15619  * Originally Released Under LGPL - original licence link has changed is not relivant.
15620  *
15621  * Fork - LGPL
15622  * <script type="text/javascript">
15623  */
15624
15625  
15626 /**
15627  * @class Roo.KeyMap
15628  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15629  * The constructor accepts the same config object as defined by {@link #addBinding}.
15630  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15631  * combination it will call the function with this signature (if the match is a multi-key
15632  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15633  * A KeyMap can also handle a string representation of keys.<br />
15634  * Usage:
15635  <pre><code>
15636 // map one key by key code
15637 var map = new Roo.KeyMap("my-element", {
15638     key: 13, // or Roo.EventObject.ENTER
15639     fn: myHandler,
15640     scope: myObject
15641 });
15642
15643 // map multiple keys to one action by string
15644 var map = new Roo.KeyMap("my-element", {
15645     key: "a\r\n\t",
15646     fn: myHandler,
15647     scope: myObject
15648 });
15649
15650 // map multiple keys to multiple actions by strings and array of codes
15651 var map = new Roo.KeyMap("my-element", [
15652     {
15653         key: [10,13],
15654         fn: function(){ alert("Return was pressed"); }
15655     }, {
15656         key: "abc",
15657         fn: function(){ alert('a, b or c was pressed'); }
15658     }, {
15659         key: "\t",
15660         ctrl:true,
15661         shift:true,
15662         fn: function(){ alert('Control + shift + tab was pressed.'); }
15663     }
15664 ]);
15665 </code></pre>
15666  * <b>Note: A KeyMap starts enabled</b>
15667  * @constructor
15668  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15669  * @param {Object} config The config (see {@link #addBinding})
15670  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15671  */
15672 Roo.KeyMap = function(el, config, eventName){
15673     this.el  = Roo.get(el);
15674     this.eventName = eventName || "keydown";
15675     this.bindings = [];
15676     if(config){
15677         this.addBinding(config);
15678     }
15679     this.enable();
15680 };
15681
15682 Roo.KeyMap.prototype = {
15683     /**
15684      * True to stop the event from bubbling and prevent the default browser action if the
15685      * key was handled by the KeyMap (defaults to false)
15686      * @type Boolean
15687      */
15688     stopEvent : false,
15689
15690     /**
15691      * Add a new binding to this KeyMap. The following config object properties are supported:
15692      * <pre>
15693 Property    Type             Description
15694 ----------  ---------------  ----------------------------------------------------------------------
15695 key         String/Array     A single keycode or an array of keycodes to handle
15696 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15697 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15698 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15699 fn          Function         The function to call when KeyMap finds the expected key combination
15700 scope       Object           The scope of the callback function
15701 </pre>
15702      *
15703      * Usage:
15704      * <pre><code>
15705 // Create a KeyMap
15706 var map = new Roo.KeyMap(document, {
15707     key: Roo.EventObject.ENTER,
15708     fn: handleKey,
15709     scope: this
15710 });
15711
15712 //Add a new binding to the existing KeyMap later
15713 map.addBinding({
15714     key: 'abc',
15715     shift: true,
15716     fn: handleKey,
15717     scope: this
15718 });
15719 </code></pre>
15720      * @param {Object/Array} config A single KeyMap config or an array of configs
15721      */
15722         addBinding : function(config){
15723         if(config instanceof Array){
15724             for(var i = 0, len = config.length; i < len; i++){
15725                 this.addBinding(config[i]);
15726             }
15727             return;
15728         }
15729         var keyCode = config.key,
15730             shift = config.shift, 
15731             ctrl = config.ctrl, 
15732             alt = config.alt,
15733             fn = config.fn,
15734             scope = config.scope;
15735         if(typeof keyCode == "string"){
15736             var ks = [];
15737             var keyString = keyCode.toUpperCase();
15738             for(var j = 0, len = keyString.length; j < len; j++){
15739                 ks.push(keyString.charCodeAt(j));
15740             }
15741             keyCode = ks;
15742         }
15743         var keyArray = keyCode instanceof Array;
15744         var handler = function(e){
15745             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15746                 var k = e.getKey();
15747                 if(keyArray){
15748                     for(var i = 0, len = keyCode.length; i < len; i++){
15749                         if(keyCode[i] == k){
15750                           if(this.stopEvent){
15751                               e.stopEvent();
15752                           }
15753                           fn.call(scope || window, k, e);
15754                           return;
15755                         }
15756                     }
15757                 }else{
15758                     if(k == keyCode){
15759                         if(this.stopEvent){
15760                            e.stopEvent();
15761                         }
15762                         fn.call(scope || window, k, e);
15763                     }
15764                 }
15765             }
15766         };
15767         this.bindings.push(handler);  
15768         },
15769
15770     /**
15771      * Shorthand for adding a single key listener
15772      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15773      * following options:
15774      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15775      * @param {Function} fn The function to call
15776      * @param {Object} scope (optional) The scope of the function
15777      */
15778     on : function(key, fn, scope){
15779         var keyCode, shift, ctrl, alt;
15780         if(typeof key == "object" && !(key instanceof Array)){
15781             keyCode = key.key;
15782             shift = key.shift;
15783             ctrl = key.ctrl;
15784             alt = key.alt;
15785         }else{
15786             keyCode = key;
15787         }
15788         this.addBinding({
15789             key: keyCode,
15790             shift: shift,
15791             ctrl: ctrl,
15792             alt: alt,
15793             fn: fn,
15794             scope: scope
15795         })
15796     },
15797
15798     // private
15799     handleKeyDown : function(e){
15800             if(this.enabled){ //just in case
15801             var b = this.bindings;
15802             for(var i = 0, len = b.length; i < len; i++){
15803                 b[i].call(this, e);
15804             }
15805             }
15806         },
15807         
15808         /**
15809          * Returns true if this KeyMap is enabled
15810          * @return {Boolean} 
15811          */
15812         isEnabled : function(){
15813             return this.enabled;  
15814         },
15815         
15816         /**
15817          * Enables this KeyMap
15818          */
15819         enable: function(){
15820                 if(!this.enabled){
15821                     this.el.on(this.eventName, this.handleKeyDown, this);
15822                     this.enabled = true;
15823                 }
15824         },
15825
15826         /**
15827          * Disable this KeyMap
15828          */
15829         disable: function(){
15830                 if(this.enabled){
15831                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15832                     this.enabled = false;
15833                 }
15834         }
15835 };/*
15836  * Based on:
15837  * Ext JS Library 1.1.1
15838  * Copyright(c) 2006-2007, Ext JS, LLC.
15839  *
15840  * Originally Released Under LGPL - original licence link has changed is not relivant.
15841  *
15842  * Fork - LGPL
15843  * <script type="text/javascript">
15844  */
15845
15846  
15847 /**
15848  * @class Roo.util.TextMetrics
15849  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15850  * wide, in pixels, a given block of text will be.
15851  * @static
15852  */
15853 Roo.util.TextMetrics = function(){
15854     var shared;
15855     return {
15856         /**
15857          * Measures the size of the specified text
15858          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15859          * that can affect the size of the rendered text
15860          * @param {String} text The text to measure
15861          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15862          * in order to accurately measure the text height
15863          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15864          */
15865         measure : function(el, text, fixedWidth){
15866             if(!shared){
15867                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15868             }
15869             shared.bind(el);
15870             shared.setFixedWidth(fixedWidth || 'auto');
15871             return shared.getSize(text);
15872         },
15873
15874         /**
15875          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15876          * the overhead of multiple calls to initialize the style properties on each measurement.
15877          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15878          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15879          * in order to accurately measure the text height
15880          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15881          */
15882         createInstance : function(el, fixedWidth){
15883             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15884         }
15885     };
15886 }();
15887
15888 /**
15889  * @class Roo.util.TextMetrics.Instance
15890  * Instance of  TextMetrics Calcuation
15891  * @constructor
15892  * Create a new TextMetrics Instance
15893  * @param {Object} bindto
15894  * @param {Boolean} fixedWidth
15895  */
15896
15897 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15898 {
15899     var ml = new Roo.Element(document.createElement('div'));
15900     document.body.appendChild(ml.dom);
15901     ml.position('absolute');
15902     ml.setLeftTop(-1000, -1000);
15903     ml.hide();
15904
15905     if(fixedWidth){
15906         ml.setWidth(fixedWidth);
15907     }
15908      
15909     var instance = {
15910         /**
15911          * Returns the size of the specified text based on the internal element's style and width properties
15912          * @param {String} text The text to measure
15913          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15914          */
15915         getSize : function(text){
15916             ml.update(text);
15917             var s = ml.getSize();
15918             ml.update('');
15919             return s;
15920         },
15921
15922         /**
15923          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15924          * that can affect the size of the rendered text
15925          * @param {String/HTMLElement} el The element, dom node or id
15926          */
15927         bind : function(el){
15928             ml.setStyle(
15929                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15930             );
15931         },
15932
15933         /**
15934          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15935          * to set a fixed width in order to accurately measure the text height.
15936          * @param {Number} width The width to set on the element
15937          */
15938         setFixedWidth : function(width){
15939             ml.setWidth(width);
15940         },
15941
15942         /**
15943          * Returns the measured width of the specified text
15944          * @param {String} text The text to measure
15945          * @return {Number} width The width in pixels
15946          */
15947         getWidth : function(text){
15948             ml.dom.style.width = 'auto';
15949             return this.getSize(text).width;
15950         },
15951
15952         /**
15953          * Returns the measured height of the specified text.  For multiline text, be sure to call
15954          * {@link #setFixedWidth} if necessary.
15955          * @param {String} text The text to measure
15956          * @return {Number} height The height in pixels
15957          */
15958         getHeight : function(text){
15959             return this.getSize(text).height;
15960         }
15961     };
15962
15963     instance.bind(bindTo);
15964
15965     return instance;
15966 };
15967
15968 // backwards compat
15969 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15970  * Based on:
15971  * Ext JS Library 1.1.1
15972  * Copyright(c) 2006-2007, Ext JS, LLC.
15973  *
15974  * Originally Released Under LGPL - original licence link has changed is not relivant.
15975  *
15976  * Fork - LGPL
15977  * <script type="text/javascript">
15978  */
15979
15980 /**
15981  * @class Roo.state.Provider
15982  * Abstract base class for state provider implementations. This class provides methods
15983  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15984  * Provider interface.
15985  */
15986 Roo.state.Provider = function(){
15987     /**
15988      * @event statechange
15989      * Fires when a state change occurs.
15990      * @param {Provider} this This state provider
15991      * @param {String} key The state key which was changed
15992      * @param {String} value The encoded value for the state
15993      */
15994     this.addEvents({
15995         "statechange": true
15996     });
15997     this.state = {};
15998     Roo.state.Provider.superclass.constructor.call(this);
15999 };
16000 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16001     /**
16002      * Returns the current value for a key
16003      * @param {String} name The key name
16004      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16005      * @return {Mixed} The state data
16006      */
16007     get : function(name, defaultValue){
16008         return typeof this.state[name] == "undefined" ?
16009             defaultValue : this.state[name];
16010     },
16011     
16012     /**
16013      * Clears a value from the state
16014      * @param {String} name The key name
16015      */
16016     clear : function(name){
16017         delete this.state[name];
16018         this.fireEvent("statechange", this, name, null);
16019     },
16020     
16021     /**
16022      * Sets the value for a key
16023      * @param {String} name The key name
16024      * @param {Mixed} value The value to set
16025      */
16026     set : function(name, value){
16027         this.state[name] = value;
16028         this.fireEvent("statechange", this, name, value);
16029     },
16030     
16031     /**
16032      * Decodes a string previously encoded with {@link #encodeValue}.
16033      * @param {String} value The value to decode
16034      * @return {Mixed} The decoded value
16035      */
16036     decodeValue : function(cookie){
16037         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16038         var matches = re.exec(unescape(cookie));
16039         if(!matches || !matches[1]) {
16040             return; // non state cookie
16041         }
16042         var type = matches[1];
16043         var v = matches[2];
16044         switch(type){
16045             case "n":
16046                 return parseFloat(v);
16047             case "d":
16048                 return new Date(Date.parse(v));
16049             case "b":
16050                 return (v == "1");
16051             case "a":
16052                 var all = [];
16053                 var values = v.split("^");
16054                 for(var i = 0, len = values.length; i < len; i++){
16055                     all.push(this.decodeValue(values[i]));
16056                 }
16057                 return all;
16058            case "o":
16059                 var all = {};
16060                 var values = v.split("^");
16061                 for(var i = 0, len = values.length; i < len; i++){
16062                     var kv = values[i].split("=");
16063                     all[kv[0]] = this.decodeValue(kv[1]);
16064                 }
16065                 return all;
16066            default:
16067                 return v;
16068         }
16069     },
16070     
16071     /**
16072      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16073      * @param {Mixed} value The value to encode
16074      * @return {String} The encoded value
16075      */
16076     encodeValue : function(v){
16077         var enc;
16078         if(typeof v == "number"){
16079             enc = "n:" + v;
16080         }else if(typeof v == "boolean"){
16081             enc = "b:" + (v ? "1" : "0");
16082         }else if(v instanceof Date){
16083             enc = "d:" + v.toGMTString();
16084         }else if(v instanceof Array){
16085             var flat = "";
16086             for(var i = 0, len = v.length; i < len; i++){
16087                 flat += this.encodeValue(v[i]);
16088                 if(i != len-1) {
16089                     flat += "^";
16090                 }
16091             }
16092             enc = "a:" + flat;
16093         }else if(typeof v == "object"){
16094             var flat = "";
16095             for(var key in v){
16096                 if(typeof v[key] != "function"){
16097                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16098                 }
16099             }
16100             enc = "o:" + flat.substring(0, flat.length-1);
16101         }else{
16102             enc = "s:" + v;
16103         }
16104         return escape(enc);        
16105     }
16106 });
16107
16108 /*
16109  * Based on:
16110  * Ext JS Library 1.1.1
16111  * Copyright(c) 2006-2007, Ext JS, LLC.
16112  *
16113  * Originally Released Under LGPL - original licence link has changed is not relivant.
16114  *
16115  * Fork - LGPL
16116  * <script type="text/javascript">
16117  */
16118 /**
16119  * @class Roo.state.Manager
16120  * This is the global state manager. By default all components that are "state aware" check this class
16121  * for state information if you don't pass them a custom state provider. In order for this class
16122  * to be useful, it must be initialized with a provider when your application initializes.
16123  <pre><code>
16124 // in your initialization function
16125 init : function(){
16126    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16127    ...
16128    // supposed you have a {@link Roo.BorderLayout}
16129    var layout = new Roo.BorderLayout(...);
16130    layout.restoreState();
16131    // or a {Roo.BasicDialog}
16132    var dialog = new Roo.BasicDialog(...);
16133    dialog.restoreState();
16134  </code></pre>
16135  * @static
16136  */
16137 Roo.state.Manager = function(){
16138     var provider = new Roo.state.Provider();
16139     
16140     return {
16141         /**
16142          * Configures the default state provider for your application
16143          * @param {Provider} stateProvider The state provider to set
16144          */
16145         setProvider : function(stateProvider){
16146             provider = stateProvider;
16147         },
16148         
16149         /**
16150          * Returns the current value for a key
16151          * @param {String} name The key name
16152          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16153          * @return {Mixed} The state data
16154          */
16155         get : function(key, defaultValue){
16156             return provider.get(key, defaultValue);
16157         },
16158         
16159         /**
16160          * Sets the value for a key
16161          * @param {String} name The key name
16162          * @param {Mixed} value The state data
16163          */
16164          set : function(key, value){
16165             provider.set(key, value);
16166         },
16167         
16168         /**
16169          * Clears a value from the state
16170          * @param {String} name The key name
16171          */
16172         clear : function(key){
16173             provider.clear(key);
16174         },
16175         
16176         /**
16177          * Gets the currently configured state provider
16178          * @return {Provider} The state provider
16179          */
16180         getProvider : function(){
16181             return provider;
16182         }
16183     };
16184 }();
16185 /*
16186  * Based on:
16187  * Ext JS Library 1.1.1
16188  * Copyright(c) 2006-2007, Ext JS, LLC.
16189  *
16190  * Originally Released Under LGPL - original licence link has changed is not relivant.
16191  *
16192  * Fork - LGPL
16193  * <script type="text/javascript">
16194  */
16195 /**
16196  * @class Roo.state.CookieProvider
16197  * @extends Roo.state.Provider
16198  * The default Provider implementation which saves state via cookies.
16199  * <br />Usage:
16200  <pre><code>
16201    var cp = new Roo.state.CookieProvider({
16202        path: "/cgi-bin/",
16203        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16204        domain: "roojs.com"
16205    })
16206    Roo.state.Manager.setProvider(cp);
16207  </code></pre>
16208  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16209  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16210  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16211  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16212  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16213  * domain the page is running on including the 'www' like 'www.roojs.com')
16214  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16215  * @constructor
16216  * Create a new CookieProvider
16217  * @param {Object} config The configuration object
16218  */
16219 Roo.state.CookieProvider = function(config){
16220     Roo.state.CookieProvider.superclass.constructor.call(this);
16221     this.path = "/";
16222     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16223     this.domain = null;
16224     this.secure = false;
16225     Roo.apply(this, config);
16226     this.state = this.readCookies();
16227 };
16228
16229 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16230     // private
16231     set : function(name, value){
16232         if(typeof value == "undefined" || value === null){
16233             this.clear(name);
16234             return;
16235         }
16236         this.setCookie(name, value);
16237         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16238     },
16239
16240     // private
16241     clear : function(name){
16242         this.clearCookie(name);
16243         Roo.state.CookieProvider.superclass.clear.call(this, name);
16244     },
16245
16246     // private
16247     readCookies : function(){
16248         var cookies = {};
16249         var c = document.cookie + ";";
16250         var re = /\s?(.*?)=(.*?);/g;
16251         var matches;
16252         while((matches = re.exec(c)) != null){
16253             var name = matches[1];
16254             var value = matches[2];
16255             if(name && name.substring(0,3) == "ys-"){
16256                 cookies[name.substr(3)] = this.decodeValue(value);
16257             }
16258         }
16259         return cookies;
16260     },
16261
16262     // private
16263     setCookie : function(name, value){
16264         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16265            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16266            ((this.path == null) ? "" : ("; path=" + this.path)) +
16267            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16268            ((this.secure == true) ? "; secure" : "");
16269     },
16270
16271     // private
16272     clearCookie : function(name){
16273         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16274            ((this.path == null) ? "" : ("; path=" + this.path)) +
16275            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16276            ((this.secure == true) ? "; secure" : "");
16277     }
16278 });/*
16279  * Based on:
16280  * Ext JS Library 1.1.1
16281  * Copyright(c) 2006-2007, Ext JS, LLC.
16282  *
16283  * Originally Released Under LGPL - original licence link has changed is not relivant.
16284  *
16285  * Fork - LGPL
16286  * <script type="text/javascript">
16287  */
16288  
16289
16290 /**
16291  * @class Roo.ComponentMgr
16292  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16293  * @static
16294  */
16295 Roo.ComponentMgr = function(){
16296     var all = new Roo.util.MixedCollection();
16297
16298     return {
16299         /**
16300          * Registers a component.
16301          * @param {Roo.Component} c The component
16302          */
16303         register : function(c){
16304             all.add(c);
16305         },
16306
16307         /**
16308          * Unregisters a component.
16309          * @param {Roo.Component} c The component
16310          */
16311         unregister : function(c){
16312             all.remove(c);
16313         },
16314
16315         /**
16316          * Returns a component by id
16317          * @param {String} id The component id
16318          */
16319         get : function(id){
16320             return all.get(id);
16321         },
16322
16323         /**
16324          * Registers a function that will be called when a specified component is added to ComponentMgr
16325          * @param {String} id The component id
16326          * @param {Funtction} fn The callback function
16327          * @param {Object} scope The scope of the callback
16328          */
16329         onAvailable : function(id, fn, scope){
16330             all.on("add", function(index, o){
16331                 if(o.id == id){
16332                     fn.call(scope || o, o);
16333                     all.un("add", fn, scope);
16334                 }
16335             });
16336         }
16337     };
16338 }();/*
16339  * Based on:
16340  * Ext JS Library 1.1.1
16341  * Copyright(c) 2006-2007, Ext JS, LLC.
16342  *
16343  * Originally Released Under LGPL - original licence link has changed is not relivant.
16344  *
16345  * Fork - LGPL
16346  * <script type="text/javascript">
16347  */
16348  
16349 /**
16350  * @class Roo.Component
16351  * @extends Roo.util.Observable
16352  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16353  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16354  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16355  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16356  * All visual components (widgets) that require rendering into a layout should subclass Component.
16357  * @constructor
16358  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16359  * 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
16360  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16361  */
16362 Roo.Component = function(config){
16363     config = config || {};
16364     if(config.tagName || config.dom || typeof config == "string"){ // element object
16365         config = {el: config, id: config.id || config};
16366     }
16367     this.initialConfig = config;
16368
16369     Roo.apply(this, config);
16370     this.addEvents({
16371         /**
16372          * @event disable
16373          * Fires after the component is disabled.
16374              * @param {Roo.Component} this
16375              */
16376         disable : true,
16377         /**
16378          * @event enable
16379          * Fires after the component is enabled.
16380              * @param {Roo.Component} this
16381              */
16382         enable : true,
16383         /**
16384          * @event beforeshow
16385          * Fires before the component is shown.  Return false to stop the show.
16386              * @param {Roo.Component} this
16387              */
16388         beforeshow : true,
16389         /**
16390          * @event show
16391          * Fires after the component is shown.
16392              * @param {Roo.Component} this
16393              */
16394         show : true,
16395         /**
16396          * @event beforehide
16397          * Fires before the component is hidden. Return false to stop the hide.
16398              * @param {Roo.Component} this
16399              */
16400         beforehide : true,
16401         /**
16402          * @event hide
16403          * Fires after the component is hidden.
16404              * @param {Roo.Component} this
16405              */
16406         hide : true,
16407         /**
16408          * @event beforerender
16409          * Fires before the component is rendered. Return false to stop the render.
16410              * @param {Roo.Component} this
16411              */
16412         beforerender : true,
16413         /**
16414          * @event render
16415          * Fires after the component is rendered.
16416              * @param {Roo.Component} this
16417              */
16418         render : true,
16419         /**
16420          * @event beforedestroy
16421          * Fires before the component is destroyed. Return false to stop the destroy.
16422              * @param {Roo.Component} this
16423              */
16424         beforedestroy : true,
16425         /**
16426          * @event destroy
16427          * Fires after the component is destroyed.
16428              * @param {Roo.Component} this
16429              */
16430         destroy : true
16431     });
16432     if(!this.id){
16433         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16434     }
16435     Roo.ComponentMgr.register(this);
16436     Roo.Component.superclass.constructor.call(this);
16437     this.initComponent();
16438     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16439         this.render(this.renderTo);
16440         delete this.renderTo;
16441     }
16442 };
16443
16444 /** @private */
16445 Roo.Component.AUTO_ID = 1000;
16446
16447 Roo.extend(Roo.Component, Roo.util.Observable, {
16448     /**
16449      * @scope Roo.Component.prototype
16450      * @type {Boolean}
16451      * true if this component is hidden. Read-only.
16452      */
16453     hidden : false,
16454     /**
16455      * @type {Boolean}
16456      * true if this component is disabled. Read-only.
16457      */
16458     disabled : false,
16459     /**
16460      * @type {Boolean}
16461      * true if this component has been rendered. Read-only.
16462      */
16463     rendered : false,
16464     
16465     /** @cfg {String} disableClass
16466      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16467      */
16468     disabledClass : "x-item-disabled",
16469         /** @cfg {Boolean} allowDomMove
16470          * Whether the component can move the Dom node when rendering (defaults to true).
16471          */
16472     allowDomMove : true,
16473     /** @cfg {String} hideMode (display|visibility)
16474      * How this component should hidden. Supported values are
16475      * "visibility" (css visibility), "offsets" (negative offset position) and
16476      * "display" (css display) - defaults to "display".
16477      */
16478     hideMode: 'display',
16479
16480     /** @private */
16481     ctype : "Roo.Component",
16482
16483     /**
16484      * @cfg {String} actionMode 
16485      * which property holds the element that used for  hide() / show() / disable() / enable()
16486      * default is 'el' for forms you probably want to set this to fieldEl 
16487      */
16488     actionMode : "el",
16489
16490     /** @private */
16491     getActionEl : function(){
16492         return this[this.actionMode];
16493     },
16494
16495     initComponent : Roo.emptyFn,
16496     /**
16497      * If this is a lazy rendering component, render it to its container element.
16498      * @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.
16499      */
16500     render : function(container, position){
16501         
16502         if(this.rendered){
16503             return this;
16504         }
16505         
16506         if(this.fireEvent("beforerender", this) === false){
16507             return false;
16508         }
16509         
16510         if(!container && this.el){
16511             this.el = Roo.get(this.el);
16512             container = this.el.dom.parentNode;
16513             this.allowDomMove = false;
16514         }
16515         this.container = Roo.get(container);
16516         this.rendered = true;
16517         if(position !== undefined){
16518             if(typeof position == 'number'){
16519                 position = this.container.dom.childNodes[position];
16520             }else{
16521                 position = Roo.getDom(position);
16522             }
16523         }
16524         this.onRender(this.container, position || null);
16525         if(this.cls){
16526             this.el.addClass(this.cls);
16527             delete this.cls;
16528         }
16529         if(this.style){
16530             this.el.applyStyles(this.style);
16531             delete this.style;
16532         }
16533         this.fireEvent("render", this);
16534         this.afterRender(this.container);
16535         if(this.hidden){
16536             this.hide();
16537         }
16538         if(this.disabled){
16539             this.disable();
16540         }
16541
16542         return this;
16543         
16544     },
16545
16546     /** @private */
16547     // default function is not really useful
16548     onRender : function(ct, position){
16549         if(this.el){
16550             this.el = Roo.get(this.el);
16551             if(this.allowDomMove !== false){
16552                 ct.dom.insertBefore(this.el.dom, position);
16553             }
16554         }
16555     },
16556
16557     /** @private */
16558     getAutoCreate : function(){
16559         var cfg = typeof this.autoCreate == "object" ?
16560                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16561         if(this.id && !cfg.id){
16562             cfg.id = this.id;
16563         }
16564         return cfg;
16565     },
16566
16567     /** @private */
16568     afterRender : Roo.emptyFn,
16569
16570     /**
16571      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16572      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16573      */
16574     destroy : function(){
16575         if(this.fireEvent("beforedestroy", this) !== false){
16576             this.purgeListeners();
16577             this.beforeDestroy();
16578             if(this.rendered){
16579                 this.el.removeAllListeners();
16580                 this.el.remove();
16581                 if(this.actionMode == "container"){
16582                     this.container.remove();
16583                 }
16584             }
16585             this.onDestroy();
16586             Roo.ComponentMgr.unregister(this);
16587             this.fireEvent("destroy", this);
16588         }
16589     },
16590
16591         /** @private */
16592     beforeDestroy : function(){
16593
16594     },
16595
16596         /** @private */
16597         onDestroy : function(){
16598
16599     },
16600
16601     /**
16602      * Returns the underlying {@link Roo.Element}.
16603      * @return {Roo.Element} The element
16604      */
16605     getEl : function(){
16606         return this.el;
16607     },
16608
16609     /**
16610      * Returns the id of this component.
16611      * @return {String}
16612      */
16613     getId : function(){
16614         return this.id;
16615     },
16616
16617     /**
16618      * Try to focus this component.
16619      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16620      * @return {Roo.Component} this
16621      */
16622     focus : function(selectText){
16623         if(this.rendered){
16624             this.el.focus();
16625             if(selectText === true){
16626                 this.el.dom.select();
16627             }
16628         }
16629         return this;
16630     },
16631
16632     /** @private */
16633     blur : function(){
16634         if(this.rendered){
16635             this.el.blur();
16636         }
16637         return this;
16638     },
16639
16640     /**
16641      * Disable this component.
16642      * @return {Roo.Component} this
16643      */
16644     disable : function(){
16645         if(this.rendered){
16646             this.onDisable();
16647         }
16648         this.disabled = true;
16649         this.fireEvent("disable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onDisable : function(){
16655         this.getActionEl().addClass(this.disabledClass);
16656         this.el.dom.disabled = true;
16657     },
16658
16659     /**
16660      * Enable this component.
16661      * @return {Roo.Component} this
16662      */
16663     enable : function(){
16664         if(this.rendered){
16665             this.onEnable();
16666         }
16667         this.disabled = false;
16668         this.fireEvent("enable", this);
16669         return this;
16670     },
16671
16672         // private
16673     onEnable : function(){
16674         this.getActionEl().removeClass(this.disabledClass);
16675         this.el.dom.disabled = false;
16676     },
16677
16678     /**
16679      * Convenience function for setting disabled/enabled by boolean.
16680      * @param {Boolean} disabled
16681      */
16682     setDisabled : function(disabled){
16683         this[disabled ? "disable" : "enable"]();
16684     },
16685
16686     /**
16687      * Show this component.
16688      * @return {Roo.Component} this
16689      */
16690     show: function(){
16691         if(this.fireEvent("beforeshow", this) !== false){
16692             this.hidden = false;
16693             if(this.rendered){
16694                 this.onShow();
16695             }
16696             this.fireEvent("show", this);
16697         }
16698         return this;
16699     },
16700
16701     // private
16702     onShow : function(){
16703         var ae = this.getActionEl();
16704         if(this.hideMode == 'visibility'){
16705             ae.dom.style.visibility = "visible";
16706         }else if(this.hideMode == 'offsets'){
16707             ae.removeClass('x-hidden');
16708         }else{
16709             ae.dom.style.display = "";
16710         }
16711     },
16712
16713     /**
16714      * Hide this component.
16715      * @return {Roo.Component} this
16716      */
16717     hide: function(){
16718         if(this.fireEvent("beforehide", this) !== false){
16719             this.hidden = true;
16720             if(this.rendered){
16721                 this.onHide();
16722             }
16723             this.fireEvent("hide", this);
16724         }
16725         return this;
16726     },
16727
16728     // private
16729     onHide : function(){
16730         var ae = this.getActionEl();
16731         if(this.hideMode == 'visibility'){
16732             ae.dom.style.visibility = "hidden";
16733         }else if(this.hideMode == 'offsets'){
16734             ae.addClass('x-hidden');
16735         }else{
16736             ae.dom.style.display = "none";
16737         }
16738     },
16739
16740     /**
16741      * Convenience function to hide or show this component by boolean.
16742      * @param {Boolean} visible True to show, false to hide
16743      * @return {Roo.Component} this
16744      */
16745     setVisible: function(visible){
16746         if(visible) {
16747             this.show();
16748         }else{
16749             this.hide();
16750         }
16751         return this;
16752     },
16753
16754     /**
16755      * Returns true if this component is visible.
16756      */
16757     isVisible : function(){
16758         return this.getActionEl().isVisible();
16759     },
16760
16761     cloneConfig : function(overrides){
16762         overrides = overrides || {};
16763         var id = overrides.id || Roo.id();
16764         var cfg = Roo.applyIf(overrides, this.initialConfig);
16765         cfg.id = id; // prevent dup id
16766         return new this.constructor(cfg);
16767     }
16768 });/*
16769  * Based on:
16770  * Ext JS Library 1.1.1
16771  * Copyright(c) 2006-2007, Ext JS, LLC.
16772  *
16773  * Originally Released Under LGPL - original licence link has changed is not relivant.
16774  *
16775  * Fork - LGPL
16776  * <script type="text/javascript">
16777  */
16778
16779 /**
16780  * @class Roo.BoxComponent
16781  * @extends Roo.Component
16782  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16783  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16784  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16785  * layout containers.
16786  * @constructor
16787  * @param {Roo.Element/String/Object} config The configuration options.
16788  */
16789 Roo.BoxComponent = function(config){
16790     Roo.Component.call(this, config);
16791     this.addEvents({
16792         /**
16793          * @event resize
16794          * Fires after the component is resized.
16795              * @param {Roo.Component} this
16796              * @param {Number} adjWidth The box-adjusted width that was set
16797              * @param {Number} adjHeight The box-adjusted height that was set
16798              * @param {Number} rawWidth The width that was originally specified
16799              * @param {Number} rawHeight The height that was originally specified
16800              */
16801         resize : true,
16802         /**
16803          * @event move
16804          * Fires after the component is moved.
16805              * @param {Roo.Component} this
16806              * @param {Number} x The new x position
16807              * @param {Number} y The new y position
16808              */
16809         move : true
16810     });
16811 };
16812
16813 Roo.extend(Roo.BoxComponent, Roo.Component, {
16814     // private, set in afterRender to signify that the component has been rendered
16815     boxReady : false,
16816     // private, used to defer height settings to subclasses
16817     deferHeight: false,
16818     /** @cfg {Number} width
16819      * width (optional) size of component
16820      */
16821      /** @cfg {Number} height
16822      * height (optional) size of component
16823      */
16824      
16825     /**
16826      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16827      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16828      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16829      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16830      * @return {Roo.BoxComponent} this
16831      */
16832     setSize : function(w, h){
16833         // support for standard size objects
16834         if(typeof w == 'object'){
16835             h = w.height;
16836             w = w.width;
16837         }
16838         // not rendered
16839         if(!this.boxReady){
16840             this.width = w;
16841             this.height = h;
16842             return this;
16843         }
16844
16845         // prevent recalcs when not needed
16846         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16847             return this;
16848         }
16849         this.lastSize = {width: w, height: h};
16850
16851         var adj = this.adjustSize(w, h);
16852         var aw = adj.width, ah = adj.height;
16853         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16854             var rz = this.getResizeEl();
16855             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16856                 rz.setSize(aw, ah);
16857             }else if(!this.deferHeight && ah !== undefined){
16858                 rz.setHeight(ah);
16859             }else if(aw !== undefined){
16860                 rz.setWidth(aw);
16861             }
16862             this.onResize(aw, ah, w, h);
16863             this.fireEvent('resize', this, aw, ah, w, h);
16864         }
16865         return this;
16866     },
16867
16868     /**
16869      * Gets the current size of the component's underlying element.
16870      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16871      */
16872     getSize : function(){
16873         return this.el.getSize();
16874     },
16875
16876     /**
16877      * Gets the current XY position of the component's underlying element.
16878      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16879      * @return {Array} The XY position of the element (e.g., [100, 200])
16880      */
16881     getPosition : function(local){
16882         if(local === true){
16883             return [this.el.getLeft(true), this.el.getTop(true)];
16884         }
16885         return this.xy || this.el.getXY();
16886     },
16887
16888     /**
16889      * Gets the current box measurements of the component's underlying element.
16890      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16891      * @returns {Object} box An object in the format {x, y, width, height}
16892      */
16893     getBox : function(local){
16894         var s = this.el.getSize();
16895         if(local){
16896             s.x = this.el.getLeft(true);
16897             s.y = this.el.getTop(true);
16898         }else{
16899             var xy = this.xy || this.el.getXY();
16900             s.x = xy[0];
16901             s.y = xy[1];
16902         }
16903         return s;
16904     },
16905
16906     /**
16907      * Sets the current box measurements of the component's underlying element.
16908      * @param {Object} box An object in the format {x, y, width, height}
16909      * @returns {Roo.BoxComponent} this
16910      */
16911     updateBox : function(box){
16912         this.setSize(box.width, box.height);
16913         this.setPagePosition(box.x, box.y);
16914         return this;
16915     },
16916
16917     // protected
16918     getResizeEl : function(){
16919         return this.resizeEl || this.el;
16920     },
16921
16922     // protected
16923     getPositionEl : function(){
16924         return this.positionEl || this.el;
16925     },
16926
16927     /**
16928      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16929      * This method fires the move event.
16930      * @param {Number} left The new left
16931      * @param {Number} top The new top
16932      * @returns {Roo.BoxComponent} this
16933      */
16934     setPosition : function(x, y){
16935         this.x = x;
16936         this.y = y;
16937         if(!this.boxReady){
16938             return this;
16939         }
16940         var adj = this.adjustPosition(x, y);
16941         var ax = adj.x, ay = adj.y;
16942
16943         var el = this.getPositionEl();
16944         if(ax !== undefined || ay !== undefined){
16945             if(ax !== undefined && ay !== undefined){
16946                 el.setLeftTop(ax, ay);
16947             }else if(ax !== undefined){
16948                 el.setLeft(ax);
16949             }else if(ay !== undefined){
16950                 el.setTop(ay);
16951             }
16952             this.onPosition(ax, ay);
16953             this.fireEvent('move', this, ax, ay);
16954         }
16955         return this;
16956     },
16957
16958     /**
16959      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16960      * This method fires the move event.
16961      * @param {Number} x The new x position
16962      * @param {Number} y The new y position
16963      * @returns {Roo.BoxComponent} this
16964      */
16965     setPagePosition : function(x, y){
16966         this.pageX = x;
16967         this.pageY = y;
16968         if(!this.boxReady){
16969             return;
16970         }
16971         if(x === undefined || y === undefined){ // cannot translate undefined points
16972             return;
16973         }
16974         var p = this.el.translatePoints(x, y);
16975         this.setPosition(p.left, p.top);
16976         return this;
16977     },
16978
16979     // private
16980     onRender : function(ct, position){
16981         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16982         if(this.resizeEl){
16983             this.resizeEl = Roo.get(this.resizeEl);
16984         }
16985         if(this.positionEl){
16986             this.positionEl = Roo.get(this.positionEl);
16987         }
16988     },
16989
16990     // private
16991     afterRender : function(){
16992         Roo.BoxComponent.superclass.afterRender.call(this);
16993         this.boxReady = true;
16994         this.setSize(this.width, this.height);
16995         if(this.x || this.y){
16996             this.setPosition(this.x, this.y);
16997         }
16998         if(this.pageX || this.pageY){
16999             this.setPagePosition(this.pageX, this.pageY);
17000         }
17001     },
17002
17003     /**
17004      * Force the component's size to recalculate based on the underlying element's current height and width.
17005      * @returns {Roo.BoxComponent} this
17006      */
17007     syncSize : function(){
17008         delete this.lastSize;
17009         this.setSize(this.el.getWidth(), this.el.getHeight());
17010         return this;
17011     },
17012
17013     /**
17014      * Called after the component is resized, this method is empty by default but can be implemented by any
17015      * subclass that needs to perform custom logic after a resize occurs.
17016      * @param {Number} adjWidth The box-adjusted width that was set
17017      * @param {Number} adjHeight The box-adjusted height that was set
17018      * @param {Number} rawWidth The width that was originally specified
17019      * @param {Number} rawHeight The height that was originally specified
17020      */
17021     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17022
17023     },
17024
17025     /**
17026      * Called after the component is moved, this method is empty by default but can be implemented by any
17027      * subclass that needs to perform custom logic after a move occurs.
17028      * @param {Number} x The new x position
17029      * @param {Number} y The new y position
17030      */
17031     onPosition : function(x, y){
17032
17033     },
17034
17035     // private
17036     adjustSize : function(w, h){
17037         if(this.autoWidth){
17038             w = 'auto';
17039         }
17040         if(this.autoHeight){
17041             h = 'auto';
17042         }
17043         return {width : w, height: h};
17044     },
17045
17046     // private
17047     adjustPosition : function(x, y){
17048         return {x : x, y: y};
17049     }
17050 });/*
17051  * Based on:
17052  * Ext JS Library 1.1.1
17053  * Copyright(c) 2006-2007, Ext JS, LLC.
17054  *
17055  * Originally Released Under LGPL - original licence link has changed is not relivant.
17056  *
17057  * Fork - LGPL
17058  * <script type="text/javascript">
17059  */
17060  (function(){ 
17061 /**
17062  * @class Roo.Layer
17063  * @extends Roo.Element
17064  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17065  * automatic maintaining of shadow/shim positions.
17066  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17067  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17068  * you can pass a string with a CSS class name. False turns off the shadow.
17069  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17070  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17071  * @cfg {String} cls CSS class to add to the element
17072  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17073  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17074  * @constructor
17075  * @param {Object} config An object with config options.
17076  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17077  */
17078
17079 Roo.Layer = function(config, existingEl){
17080     config = config || {};
17081     var dh = Roo.DomHelper;
17082     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17083     if(existingEl){
17084         this.dom = Roo.getDom(existingEl);
17085     }
17086     if(!this.dom){
17087         var o = config.dh || {tag: "div", cls: "x-layer"};
17088         this.dom = dh.append(pel, o);
17089     }
17090     if(config.cls){
17091         this.addClass(config.cls);
17092     }
17093     this.constrain = config.constrain !== false;
17094     this.visibilityMode = Roo.Element.VISIBILITY;
17095     if(config.id){
17096         this.id = this.dom.id = config.id;
17097     }else{
17098         this.id = Roo.id(this.dom);
17099     }
17100     this.zindex = config.zindex || this.getZIndex();
17101     this.position("absolute", this.zindex);
17102     if(config.shadow){
17103         this.shadowOffset = config.shadowOffset || 4;
17104         this.shadow = new Roo.Shadow({
17105             offset : this.shadowOffset,
17106             mode : config.shadow
17107         });
17108     }else{
17109         this.shadowOffset = 0;
17110     }
17111     this.useShim = config.shim !== false && Roo.useShims;
17112     this.useDisplay = config.useDisplay;
17113     this.hide();
17114 };
17115
17116 var supr = Roo.Element.prototype;
17117
17118 // shims are shared among layer to keep from having 100 iframes
17119 var shims = [];
17120
17121 Roo.extend(Roo.Layer, Roo.Element, {
17122
17123     getZIndex : function(){
17124         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17125     },
17126
17127     getShim : function(){
17128         if(!this.useShim){
17129             return null;
17130         }
17131         if(this.shim){
17132             return this.shim;
17133         }
17134         var shim = shims.shift();
17135         if(!shim){
17136             shim = this.createShim();
17137             shim.enableDisplayMode('block');
17138             shim.dom.style.display = 'none';
17139             shim.dom.style.visibility = 'visible';
17140         }
17141         var pn = this.dom.parentNode;
17142         if(shim.dom.parentNode != pn){
17143             pn.insertBefore(shim.dom, this.dom);
17144         }
17145         shim.setStyle('z-index', this.getZIndex()-2);
17146         this.shim = shim;
17147         return shim;
17148     },
17149
17150     hideShim : function(){
17151         if(this.shim){
17152             this.shim.setDisplayed(false);
17153             shims.push(this.shim);
17154             delete this.shim;
17155         }
17156     },
17157
17158     disableShadow : function(){
17159         if(this.shadow){
17160             this.shadowDisabled = true;
17161             this.shadow.hide();
17162             this.lastShadowOffset = this.shadowOffset;
17163             this.shadowOffset = 0;
17164         }
17165     },
17166
17167     enableShadow : function(show){
17168         if(this.shadow){
17169             this.shadowDisabled = false;
17170             this.shadowOffset = this.lastShadowOffset;
17171             delete this.lastShadowOffset;
17172             if(show){
17173                 this.sync(true);
17174             }
17175         }
17176     },
17177
17178     // private
17179     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17180     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17181     sync : function(doShow){
17182         var sw = this.shadow;
17183         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17184             var sh = this.getShim();
17185
17186             var w = this.getWidth(),
17187                 h = this.getHeight();
17188
17189             var l = this.getLeft(true),
17190                 t = this.getTop(true);
17191
17192             if(sw && !this.shadowDisabled){
17193                 if(doShow && !sw.isVisible()){
17194                     sw.show(this);
17195                 }else{
17196                     sw.realign(l, t, w, h);
17197                 }
17198                 if(sh){
17199                     if(doShow){
17200                        sh.show();
17201                     }
17202                     // fit the shim behind the shadow, so it is shimmed too
17203                     var a = sw.adjusts, s = sh.dom.style;
17204                     s.left = (Math.min(l, l+a.l))+"px";
17205                     s.top = (Math.min(t, t+a.t))+"px";
17206                     s.width = (w+a.w)+"px";
17207                     s.height = (h+a.h)+"px";
17208                 }
17209             }else if(sh){
17210                 if(doShow){
17211                    sh.show();
17212                 }
17213                 sh.setSize(w, h);
17214                 sh.setLeftTop(l, t);
17215             }
17216             
17217         }
17218     },
17219
17220     // private
17221     destroy : function(){
17222         this.hideShim();
17223         if(this.shadow){
17224             this.shadow.hide();
17225         }
17226         this.removeAllListeners();
17227         var pn = this.dom.parentNode;
17228         if(pn){
17229             pn.removeChild(this.dom);
17230         }
17231         Roo.Element.uncache(this.id);
17232     },
17233
17234     remove : function(){
17235         this.destroy();
17236     },
17237
17238     // private
17239     beginUpdate : function(){
17240         this.updating = true;
17241     },
17242
17243     // private
17244     endUpdate : function(){
17245         this.updating = false;
17246         this.sync(true);
17247     },
17248
17249     // private
17250     hideUnders : function(negOffset){
17251         if(this.shadow){
17252             this.shadow.hide();
17253         }
17254         this.hideShim();
17255     },
17256
17257     // private
17258     constrainXY : function(){
17259         if(this.constrain){
17260             var vw = Roo.lib.Dom.getViewWidth(),
17261                 vh = Roo.lib.Dom.getViewHeight();
17262             var s = Roo.get(document).getScroll();
17263
17264             var xy = this.getXY();
17265             var x = xy[0], y = xy[1];   
17266             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17267             // only move it if it needs it
17268             var moved = false;
17269             // first validate right/bottom
17270             if((x + w) > vw+s.left){
17271                 x = vw - w - this.shadowOffset;
17272                 moved = true;
17273             }
17274             if((y + h) > vh+s.top){
17275                 y = vh - h - this.shadowOffset;
17276                 moved = true;
17277             }
17278             // then make sure top/left isn't negative
17279             if(x < s.left){
17280                 x = s.left;
17281                 moved = true;
17282             }
17283             if(y < s.top){
17284                 y = s.top;
17285                 moved = true;
17286             }
17287             if(moved){
17288                 if(this.avoidY){
17289                     var ay = this.avoidY;
17290                     if(y <= ay && (y+h) >= ay){
17291                         y = ay-h-5;   
17292                     }
17293                 }
17294                 xy = [x, y];
17295                 this.storeXY(xy);
17296                 supr.setXY.call(this, xy);
17297                 this.sync();
17298             }
17299         }
17300     },
17301
17302     isVisible : function(){
17303         return this.visible;    
17304     },
17305
17306     // private
17307     showAction : function(){
17308         this.visible = true; // track visibility to prevent getStyle calls
17309         if(this.useDisplay === true){
17310             this.setDisplayed("");
17311         }else if(this.lastXY){
17312             supr.setXY.call(this, this.lastXY);
17313         }else if(this.lastLT){
17314             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17315         }
17316     },
17317
17318     // private
17319     hideAction : function(){
17320         this.visible = false;
17321         if(this.useDisplay === true){
17322             this.setDisplayed(false);
17323         }else{
17324             this.setLeftTop(-10000,-10000);
17325         }
17326     },
17327
17328     // overridden Element method
17329     setVisible : function(v, a, d, c, e){
17330         if(v){
17331             this.showAction();
17332         }
17333         if(a && v){
17334             var cb = function(){
17335                 this.sync(true);
17336                 if(c){
17337                     c();
17338                 }
17339             }.createDelegate(this);
17340             supr.setVisible.call(this, true, true, d, cb, e);
17341         }else{
17342             if(!v){
17343                 this.hideUnders(true);
17344             }
17345             var cb = c;
17346             if(a){
17347                 cb = function(){
17348                     this.hideAction();
17349                     if(c){
17350                         c();
17351                     }
17352                 }.createDelegate(this);
17353             }
17354             supr.setVisible.call(this, v, a, d, cb, e);
17355             if(v){
17356                 this.sync(true);
17357             }else if(!a){
17358                 this.hideAction();
17359             }
17360         }
17361     },
17362
17363     storeXY : function(xy){
17364         delete this.lastLT;
17365         this.lastXY = xy;
17366     },
17367
17368     storeLeftTop : function(left, top){
17369         delete this.lastXY;
17370         this.lastLT = [left, top];
17371     },
17372
17373     // private
17374     beforeFx : function(){
17375         this.beforeAction();
17376         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17377     },
17378
17379     // private
17380     afterFx : function(){
17381         Roo.Layer.superclass.afterFx.apply(this, arguments);
17382         this.sync(this.isVisible());
17383     },
17384
17385     // private
17386     beforeAction : function(){
17387         if(!this.updating && this.shadow){
17388             this.shadow.hide();
17389         }
17390     },
17391
17392     // overridden Element method
17393     setLeft : function(left){
17394         this.storeLeftTop(left, this.getTop(true));
17395         supr.setLeft.apply(this, arguments);
17396         this.sync();
17397     },
17398
17399     setTop : function(top){
17400         this.storeLeftTop(this.getLeft(true), top);
17401         supr.setTop.apply(this, arguments);
17402         this.sync();
17403     },
17404
17405     setLeftTop : function(left, top){
17406         this.storeLeftTop(left, top);
17407         supr.setLeftTop.apply(this, arguments);
17408         this.sync();
17409     },
17410
17411     setXY : function(xy, a, d, c, e){
17412         this.fixDisplay();
17413         this.beforeAction();
17414         this.storeXY(xy);
17415         var cb = this.createCB(c);
17416         supr.setXY.call(this, xy, a, d, cb, e);
17417         if(!a){
17418             cb();
17419         }
17420     },
17421
17422     // private
17423     createCB : function(c){
17424         var el = this;
17425         return function(){
17426             el.constrainXY();
17427             el.sync(true);
17428             if(c){
17429                 c();
17430             }
17431         };
17432     },
17433
17434     // overridden Element method
17435     setX : function(x, a, d, c, e){
17436         this.setXY([x, this.getY()], a, d, c, e);
17437     },
17438
17439     // overridden Element method
17440     setY : function(y, a, d, c, e){
17441         this.setXY([this.getX(), y], a, d, c, e);
17442     },
17443
17444     // overridden Element method
17445     setSize : function(w, h, a, d, c, e){
17446         this.beforeAction();
17447         var cb = this.createCB(c);
17448         supr.setSize.call(this, w, h, a, d, cb, e);
17449         if(!a){
17450             cb();
17451         }
17452     },
17453
17454     // overridden Element method
17455     setWidth : function(w, a, d, c, e){
17456         this.beforeAction();
17457         var cb = this.createCB(c);
17458         supr.setWidth.call(this, w, a, d, cb, e);
17459         if(!a){
17460             cb();
17461         }
17462     },
17463
17464     // overridden Element method
17465     setHeight : function(h, a, d, c, e){
17466         this.beforeAction();
17467         var cb = this.createCB(c);
17468         supr.setHeight.call(this, h, a, d, cb, e);
17469         if(!a){
17470             cb();
17471         }
17472     },
17473
17474     // overridden Element method
17475     setBounds : function(x, y, w, h, a, d, c, e){
17476         this.beforeAction();
17477         var cb = this.createCB(c);
17478         if(!a){
17479             this.storeXY([x, y]);
17480             supr.setXY.call(this, [x, y]);
17481             supr.setSize.call(this, w, h, a, d, cb, e);
17482             cb();
17483         }else{
17484             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17485         }
17486         return this;
17487     },
17488     
17489     /**
17490      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17491      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17492      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17493      * @param {Number} zindex The new z-index to set
17494      * @return {this} The Layer
17495      */
17496     setZIndex : function(zindex){
17497         this.zindex = zindex;
17498         this.setStyle("z-index", zindex + 2);
17499         if(this.shadow){
17500             this.shadow.setZIndex(zindex + 1);
17501         }
17502         if(this.shim){
17503             this.shim.setStyle("z-index", zindex);
17504         }
17505     }
17506 });
17507 })();/*
17508  * Original code for Roojs - LGPL
17509  * <script type="text/javascript">
17510  */
17511  
17512 /**
17513  * @class Roo.XComponent
17514  * A delayed Element creator...
17515  * Or a way to group chunks of interface together.
17516  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17517  *  used in conjunction with XComponent.build() it will create an instance of each element,
17518  *  then call addxtype() to build the User interface.
17519  * 
17520  * Mypart.xyx = new Roo.XComponent({
17521
17522     parent : 'Mypart.xyz', // empty == document.element.!!
17523     order : '001',
17524     name : 'xxxx'
17525     region : 'xxxx'
17526     disabled : function() {} 
17527      
17528     tree : function() { // return an tree of xtype declared components
17529         var MODULE = this;
17530         return 
17531         {
17532             xtype : 'NestedLayoutPanel',
17533             // technicall
17534         }
17535      ]
17536  *})
17537  *
17538  *
17539  * It can be used to build a big heiracy, with parent etc.
17540  * or you can just use this to render a single compoent to a dom element
17541  * MYPART.render(Roo.Element | String(id) | dom_element )
17542  *
17543  *
17544  * Usage patterns.
17545  *
17546  * Classic Roo
17547  *
17548  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17549  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17550  *
17551  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17552  *
17553  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17554  * - if mulitple topModules exist, the last one is defined as the top module.
17555  *
17556  * Embeded Roo
17557  * 
17558  * When the top level or multiple modules are to embedded into a existing HTML page,
17559  * the parent element can container '#id' of the element where the module will be drawn.
17560  *
17561  * Bootstrap Roo
17562  *
17563  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17564  * it relies more on a include mechanism, where sub modules are included into an outer page.
17565  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17566  * 
17567  * Bootstrap Roo Included elements
17568  *
17569  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17570  * hence confusing the component builder as it thinks there are multiple top level elements. 
17571  *
17572  * String Over-ride & Translations
17573  *
17574  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17575  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17576  * are needed. @see Roo.XComponent.overlayString  
17577  * 
17578  * 
17579  * 
17580  * @extends Roo.util.Observable
17581  * @constructor
17582  * @param cfg {Object} configuration of component
17583  * 
17584  */
17585 Roo.XComponent = function(cfg) {
17586     Roo.apply(this, cfg);
17587     this.addEvents({ 
17588         /**
17589              * @event built
17590              * Fires when this the componnt is built
17591              * @param {Roo.XComponent} c the component
17592              */
17593         'built' : true
17594         
17595     });
17596     this.region = this.region || 'center'; // default..
17597     Roo.XComponent.register(this);
17598     this.modules = false;
17599     this.el = false; // where the layout goes..
17600     
17601     
17602 }
17603 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17604     /**
17605      * @property el
17606      * The created element (with Roo.factory())
17607      * @type {Roo.Layout}
17608      */
17609     el  : false,
17610     
17611     /**
17612      * @property el
17613      * for BC  - use el in new code
17614      * @type {Roo.Layout}
17615      */
17616     panel : false,
17617     
17618     /**
17619      * @property layout
17620      * for BC  - use el in new code
17621      * @type {Roo.Layout}
17622      */
17623     layout : false,
17624     
17625      /**
17626      * @cfg {Function|boolean} disabled
17627      * If this module is disabled by some rule, return true from the funtion
17628      */
17629     disabled : false,
17630     
17631     /**
17632      * @cfg {String} parent 
17633      * Name of parent element which it get xtype added to..
17634      */
17635     parent: false,
17636     
17637     /**
17638      * @cfg {String} order
17639      * Used to set the order in which elements are created (usefull for multiple tabs)
17640      */
17641     
17642     order : false,
17643     /**
17644      * @cfg {String} name
17645      * String to display while loading.
17646      */
17647     name : false,
17648     /**
17649      * @cfg {String} region
17650      * Region to render component to (defaults to center)
17651      */
17652     region : 'center',
17653     
17654     /**
17655      * @cfg {Array} items
17656      * A single item array - the first element is the root of the tree..
17657      * It's done this way to stay compatible with the Xtype system...
17658      */
17659     items : false,
17660     
17661     /**
17662      * @property _tree
17663      * The method that retuns the tree of parts that make up this compoennt 
17664      * @type {function}
17665      */
17666     _tree  : false,
17667     
17668      /**
17669      * render
17670      * render element to dom or tree
17671      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17672      */
17673     
17674     render : function(el)
17675     {
17676         
17677         el = el || false;
17678         var hp = this.parent ? 1 : 0;
17679         Roo.debug &&  Roo.log(this);
17680         
17681         var tree = this._tree ? this._tree() : this.tree();
17682
17683         
17684         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17685             // if parent is a '#.....' string, then let's use that..
17686             var ename = this.parent.substr(1);
17687             this.parent = false;
17688             Roo.debug && Roo.log(ename);
17689             switch (ename) {
17690                 case 'bootstrap-body':
17691                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17692                         // this is the BorderLayout standard?
17693                        this.parent = { el : true };
17694                        break;
17695                     }
17696                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17697                         // need to insert stuff...
17698                         this.parent =  {
17699                              el : new Roo.bootstrap.layout.Border({
17700                                  el : document.body, 
17701                      
17702                                  center: {
17703                                     titlebar: false,
17704                                     autoScroll:false,
17705                                     closeOnTab: true,
17706                                     tabPosition: 'top',
17707                                       //resizeTabs: true,
17708                                     alwaysShowTabs: true,
17709                                     hideTabs: false
17710                                      //minTabWidth: 140
17711                                  }
17712                              })
17713                         
17714                          };
17715                          break;
17716                     }
17717                          
17718                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17719                         this.parent = { el :  new  Roo.bootstrap.Body() };
17720                         Roo.debug && Roo.log("setting el to doc body");
17721                          
17722                     } else {
17723                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17724                     }
17725                     break;
17726                 case 'bootstrap':
17727                     this.parent = { el : true};
17728                     // fall through
17729                 default:
17730                     el = Roo.get(ename);
17731                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17732                         this.parent = { el : true};
17733                     }
17734                     
17735                     break;
17736             }
17737                 
17738             
17739             if (!el && !this.parent) {
17740                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17741                 return;
17742             }
17743         }
17744         
17745         Roo.debug && Roo.log("EL:");
17746         Roo.debug && Roo.log(el);
17747         Roo.debug && Roo.log("this.parent.el:");
17748         Roo.debug && Roo.log(this.parent.el);
17749         
17750
17751         // altertive root elements ??? - we need a better way to indicate these.
17752         var is_alt = Roo.XComponent.is_alt ||
17753                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17754                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17755                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17756         
17757         
17758         
17759         if (!this.parent && is_alt) {
17760             //el = Roo.get(document.body);
17761             this.parent = { el : true };
17762         }
17763             
17764             
17765         
17766         if (!this.parent) {
17767             
17768             Roo.debug && Roo.log("no parent - creating one");
17769             
17770             el = el ? Roo.get(el) : false;      
17771             
17772             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17773                 
17774                 this.parent =  {
17775                     el : new Roo.bootstrap.layout.Border({
17776                         el: el || document.body,
17777                     
17778                         center: {
17779                             titlebar: false,
17780                             autoScroll:false,
17781                             closeOnTab: true,
17782                             tabPosition: 'top',
17783                              //resizeTabs: true,
17784                             alwaysShowTabs: false,
17785                             hideTabs: true,
17786                             minTabWidth: 140,
17787                             overflow: 'visible'
17788                          }
17789                      })
17790                 };
17791             } else {
17792             
17793                 // it's a top level one..
17794                 this.parent =  {
17795                     el : new Roo.BorderLayout(el || document.body, {
17796                         center: {
17797                             titlebar: false,
17798                             autoScroll:false,
17799                             closeOnTab: true,
17800                             tabPosition: 'top',
17801                              //resizeTabs: true,
17802                             alwaysShowTabs: el && hp? false :  true,
17803                             hideTabs: el || !hp ? true :  false,
17804                             minTabWidth: 140
17805                          }
17806                     })
17807                 };
17808             }
17809         }
17810         
17811         if (!this.parent.el) {
17812                 // probably an old style ctor, which has been disabled.
17813                 return;
17814
17815         }
17816                 // The 'tree' method is  '_tree now' 
17817             
17818         tree.region = tree.region || this.region;
17819         var is_body = false;
17820         if (this.parent.el === true) {
17821             // bootstrap... - body..
17822             if (el) {
17823                 tree.el = el;
17824             }
17825             this.parent.el = Roo.factory(tree);
17826             is_body = true;
17827         }
17828         
17829         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17830         this.fireEvent('built', this);
17831         
17832         this.panel = this.el;
17833         this.layout = this.panel.layout;
17834         this.parentLayout = this.parent.layout  || false;  
17835          
17836     }
17837     
17838 });
17839
17840 Roo.apply(Roo.XComponent, {
17841     /**
17842      * @property  hideProgress
17843      * true to disable the building progress bar.. usefull on single page renders.
17844      * @type Boolean
17845      */
17846     hideProgress : false,
17847     /**
17848      * @property  buildCompleted
17849      * True when the builder has completed building the interface.
17850      * @type Boolean
17851      */
17852     buildCompleted : false,
17853      
17854     /**
17855      * @property  topModule
17856      * the upper most module - uses document.element as it's constructor.
17857      * @type Object
17858      */
17859      
17860     topModule  : false,
17861       
17862     /**
17863      * @property  modules
17864      * array of modules to be created by registration system.
17865      * @type {Array} of Roo.XComponent
17866      */
17867     
17868     modules : [],
17869     /**
17870      * @property  elmodules
17871      * array of modules to be created by which use #ID 
17872      * @type {Array} of Roo.XComponent
17873      */
17874      
17875     elmodules : [],
17876
17877      /**
17878      * @property  is_alt
17879      * Is an alternative Root - normally used by bootstrap or other systems,
17880      *    where the top element in the tree can wrap 'body' 
17881      * @type {boolean}  (default false)
17882      */
17883      
17884     is_alt : false,
17885     /**
17886      * @property  build_from_html
17887      * Build elements from html - used by bootstrap HTML stuff 
17888      *    - this is cleared after build is completed
17889      * @type {boolean}    (default false)
17890      */
17891      
17892     build_from_html : false,
17893     /**
17894      * Register components to be built later.
17895      *
17896      * This solves the following issues
17897      * - Building is not done on page load, but after an authentication process has occured.
17898      * - Interface elements are registered on page load
17899      * - Parent Interface elements may not be loaded before child, so this handles that..
17900      * 
17901      *
17902      * example:
17903      * 
17904      * MyApp.register({
17905           order : '000001',
17906           module : 'Pman.Tab.projectMgr',
17907           region : 'center',
17908           parent : 'Pman.layout',
17909           disabled : false,  // or use a function..
17910         })
17911      
17912      * * @param {Object} details about module
17913      */
17914     register : function(obj) {
17915                 
17916         Roo.XComponent.event.fireEvent('register', obj);
17917         switch(typeof(obj.disabled) ) {
17918                 
17919             case 'undefined':
17920                 break;
17921             
17922             case 'function':
17923                 if ( obj.disabled() ) {
17924                         return;
17925                 }
17926                 break;
17927             
17928             default:
17929                 if (obj.disabled || obj.region == '#disabled') {
17930                         return;
17931                 }
17932                 break;
17933         }
17934                 
17935         this.modules.push(obj);
17936          
17937     },
17938     /**
17939      * convert a string to an object..
17940      * eg. 'AAA.BBB' -> finds AAA.BBB
17941
17942      */
17943     
17944     toObject : function(str)
17945     {
17946         if (!str || typeof(str) == 'object') {
17947             return str;
17948         }
17949         if (str.substring(0,1) == '#') {
17950             return str;
17951         }
17952
17953         var ar = str.split('.');
17954         var rt, o;
17955         rt = ar.shift();
17956             /** eval:var:o */
17957         try {
17958             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17959         } catch (e) {
17960             throw "Module not found : " + str;
17961         }
17962         
17963         if (o === false) {
17964             throw "Module not found : " + str;
17965         }
17966         Roo.each(ar, function(e) {
17967             if (typeof(o[e]) == 'undefined') {
17968                 throw "Module not found : " + str;
17969             }
17970             o = o[e];
17971         });
17972         
17973         return o;
17974         
17975     },
17976     
17977     
17978     /**
17979      * move modules into their correct place in the tree..
17980      * 
17981      */
17982     preBuild : function ()
17983     {
17984         var _t = this;
17985         Roo.each(this.modules , function (obj)
17986         {
17987             Roo.XComponent.event.fireEvent('beforebuild', obj);
17988             
17989             var opar = obj.parent;
17990             try { 
17991                 obj.parent = this.toObject(opar);
17992             } catch(e) {
17993                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17994                 return;
17995             }
17996             
17997             if (!obj.parent) {
17998                 Roo.debug && Roo.log("GOT top level module");
17999                 Roo.debug && Roo.log(obj);
18000                 obj.modules = new Roo.util.MixedCollection(false, 
18001                     function(o) { return o.order + '' }
18002                 );
18003                 this.topModule = obj;
18004                 return;
18005             }
18006                         // parent is a string (usually a dom element name..)
18007             if (typeof(obj.parent) == 'string') {
18008                 this.elmodules.push(obj);
18009                 return;
18010             }
18011             if (obj.parent.constructor != Roo.XComponent) {
18012                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18013             }
18014             if (!obj.parent.modules) {
18015                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18016                     function(o) { return o.order + '' }
18017                 );
18018             }
18019             if (obj.parent.disabled) {
18020                 obj.disabled = true;
18021             }
18022             obj.parent.modules.add(obj);
18023         }, this);
18024     },
18025     
18026      /**
18027      * make a list of modules to build.
18028      * @return {Array} list of modules. 
18029      */ 
18030     
18031     buildOrder : function()
18032     {
18033         var _this = this;
18034         var cmp = function(a,b) {   
18035             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18036         };
18037         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18038             throw "No top level modules to build";
18039         }
18040         
18041         // make a flat list in order of modules to build.
18042         var mods = this.topModule ? [ this.topModule ] : [];
18043                 
18044         
18045         // elmodules (is a list of DOM based modules )
18046         Roo.each(this.elmodules, function(e) {
18047             mods.push(e);
18048             if (!this.topModule &&
18049                 typeof(e.parent) == 'string' &&
18050                 e.parent.substring(0,1) == '#' &&
18051                 Roo.get(e.parent.substr(1))
18052                ) {
18053                 
18054                 _this.topModule = e;
18055             }
18056             
18057         });
18058
18059         
18060         // add modules to their parents..
18061         var addMod = function(m) {
18062             Roo.debug && Roo.log("build Order: add: " + m.name);
18063                 
18064             mods.push(m);
18065             if (m.modules && !m.disabled) {
18066                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18067                 m.modules.keySort('ASC',  cmp );
18068                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18069     
18070                 m.modules.each(addMod);
18071             } else {
18072                 Roo.debug && Roo.log("build Order: no child modules");
18073             }
18074             // not sure if this is used any more..
18075             if (m.finalize) {
18076                 m.finalize.name = m.name + " (clean up) ";
18077                 mods.push(m.finalize);
18078             }
18079             
18080         }
18081         if (this.topModule && this.topModule.modules) { 
18082             this.topModule.modules.keySort('ASC',  cmp );
18083             this.topModule.modules.each(addMod);
18084         } 
18085         return mods;
18086     },
18087     
18088      /**
18089      * Build the registered modules.
18090      * @param {Object} parent element.
18091      * @param {Function} optional method to call after module has been added.
18092      * 
18093      */ 
18094    
18095     build : function(opts) 
18096     {
18097         
18098         if (typeof(opts) != 'undefined') {
18099             Roo.apply(this,opts);
18100         }
18101         
18102         this.preBuild();
18103         var mods = this.buildOrder();
18104       
18105         //this.allmods = mods;
18106         //Roo.debug && Roo.log(mods);
18107         //return;
18108         if (!mods.length) { // should not happen
18109             throw "NO modules!!!";
18110         }
18111         
18112         
18113         var msg = "Building Interface...";
18114         // flash it up as modal - so we store the mask!?
18115         if (!this.hideProgress && Roo.MessageBox) {
18116             Roo.MessageBox.show({ title: 'loading' });
18117             Roo.MessageBox.show({
18118                title: "Please wait...",
18119                msg: msg,
18120                width:450,
18121                progress:true,
18122                buttons : false,
18123                closable:false,
18124                modal: false
18125               
18126             });
18127         }
18128         var total = mods.length;
18129         
18130         var _this = this;
18131         var progressRun = function() {
18132             if (!mods.length) {
18133                 Roo.debug && Roo.log('hide?');
18134                 if (!this.hideProgress && Roo.MessageBox) {
18135                     Roo.MessageBox.hide();
18136                 }
18137                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18138                 
18139                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18140                 
18141                 // THE END...
18142                 return false;   
18143             }
18144             
18145             var m = mods.shift();
18146             
18147             
18148             Roo.debug && Roo.log(m);
18149             // not sure if this is supported any more.. - modules that are are just function
18150             if (typeof(m) == 'function') { 
18151                 m.call(this);
18152                 return progressRun.defer(10, _this);
18153             } 
18154             
18155             
18156             msg = "Building Interface " + (total  - mods.length) + 
18157                     " of " + total + 
18158                     (m.name ? (' - ' + m.name) : '');
18159                         Roo.debug && Roo.log(msg);
18160             if (!_this.hideProgress &&  Roo.MessageBox) { 
18161                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18162             }
18163             
18164          
18165             // is the module disabled?
18166             var disabled = (typeof(m.disabled) == 'function') ?
18167                 m.disabled.call(m.module.disabled) : m.disabled;    
18168             
18169             
18170             if (disabled) {
18171                 return progressRun(); // we do not update the display!
18172             }
18173             
18174             // now build 
18175             
18176                         
18177                         
18178             m.render();
18179             // it's 10 on top level, and 1 on others??? why...
18180             return progressRun.defer(10, _this);
18181              
18182         }
18183         progressRun.defer(1, _this);
18184      
18185         
18186         
18187     },
18188     /**
18189      * Overlay a set of modified strings onto a component
18190      * This is dependant on our builder exporting the strings and 'named strings' elements.
18191      * 
18192      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18193      * @param {Object} associative array of 'named' string and it's new value.
18194      * 
18195      */
18196         overlayStrings : function( component, strings )
18197     {
18198         if (typeof(component['_named_strings']) == 'undefined') {
18199             throw "ERROR: component does not have _named_strings";
18200         }
18201         for ( var k in strings ) {
18202             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18203             if (md !== false) {
18204                 component['_strings'][md] = strings[k];
18205             } else {
18206                 Roo.log('could not find named string: ' + k + ' in');
18207                 Roo.log(component);
18208             }
18209             
18210         }
18211         
18212     },
18213     
18214         
18215         /**
18216          * Event Object.
18217          *
18218          *
18219          */
18220         event: false, 
18221     /**
18222          * wrapper for event.on - aliased later..  
18223          * Typically use to register a event handler for register:
18224          *
18225          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18226          *
18227          */
18228     on : false
18229    
18230     
18231     
18232 });
18233
18234 Roo.XComponent.event = new Roo.util.Observable({
18235                 events : { 
18236                         /**
18237                          * @event register
18238                          * Fires when an Component is registered,
18239                          * set the disable property on the Component to stop registration.
18240                          * @param {Roo.XComponent} c the component being registerd.
18241                          * 
18242                          */
18243                         'register' : true,
18244             /**
18245                          * @event beforebuild
18246                          * Fires before each Component is built
18247                          * can be used to apply permissions.
18248                          * @param {Roo.XComponent} c the component being registerd.
18249                          * 
18250                          */
18251                         'beforebuild' : true,
18252                         /**
18253                          * @event buildcomplete
18254                          * Fires on the top level element when all elements have been built
18255                          * @param {Roo.XComponent} the top level component.
18256                          */
18257                         'buildcomplete' : true
18258                         
18259                 }
18260 });
18261
18262 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18263  //
18264  /**
18265  * marked - a markdown parser
18266  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18267  * https://github.com/chjj/marked
18268  */
18269
18270
18271 /**
18272  *
18273  * Roo.Markdown - is a very crude wrapper around marked..
18274  *
18275  * usage:
18276  * 
18277  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18278  * 
18279  * Note: move the sample code to the bottom of this
18280  * file before uncommenting it.
18281  *
18282  */
18283
18284 Roo.Markdown = {};
18285 Roo.Markdown.toHtml = function(text) {
18286     
18287     var c = new Roo.Markdown.marked.setOptions({
18288             renderer: new Roo.Markdown.marked.Renderer(),
18289             gfm: true,
18290             tables: true,
18291             breaks: false,
18292             pedantic: false,
18293             sanitize: false,
18294             smartLists: true,
18295             smartypants: false
18296           });
18297     // A FEW HACKS!!?
18298     
18299     text = text.replace(/\\\n/g,' ');
18300     return Roo.Markdown.marked(text);
18301 };
18302 //
18303 // converter
18304 //
18305 // Wraps all "globals" so that the only thing
18306 // exposed is makeHtml().
18307 //
18308 (function() {
18309     
18310      /**
18311          * eval:var:escape
18312          * eval:var:unescape
18313          * eval:var:replace
18314          */
18315       
18316     /**
18317      * Helpers
18318      */
18319     
18320     var escape = function (html, encode) {
18321       return html
18322         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18323         .replace(/</g, '&lt;')
18324         .replace(/>/g, '&gt;')
18325         .replace(/"/g, '&quot;')
18326         .replace(/'/g, '&#39;');
18327     }
18328     
18329     var unescape = function (html) {
18330         // explicitly match decimal, hex, and named HTML entities 
18331       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18332         n = n.toLowerCase();
18333         if (n === 'colon') { return ':'; }
18334         if (n.charAt(0) === '#') {
18335           return n.charAt(1) === 'x'
18336             ? String.fromCharCode(parseInt(n.substring(2), 16))
18337             : String.fromCharCode(+n.substring(1));
18338         }
18339         return '';
18340       });
18341     }
18342     
18343     var replace = function (regex, opt) {
18344       regex = regex.source;
18345       opt = opt || '';
18346       return function self(name, val) {
18347         if (!name) { return new RegExp(regex, opt); }
18348         val = val.source || val;
18349         val = val.replace(/(^|[^\[])\^/g, '$1');
18350         regex = regex.replace(name, val);
18351         return self;
18352       };
18353     }
18354
18355
18356          /**
18357          * eval:var:noop
18358     */
18359     var noop = function () {}
18360     noop.exec = noop;
18361     
18362          /**
18363          * eval:var:merge
18364     */
18365     var merge = function (obj) {
18366       var i = 1
18367         , target
18368         , key;
18369     
18370       for (; i < arguments.length; i++) {
18371         target = arguments[i];
18372         for (key in target) {
18373           if (Object.prototype.hasOwnProperty.call(target, key)) {
18374             obj[key] = target[key];
18375           }
18376         }
18377       }
18378     
18379       return obj;
18380     }
18381     
18382     
18383     /**
18384      * Block-Level Grammar
18385      */
18386     
18387     
18388     
18389     
18390     var block = {
18391       newline: /^\n+/,
18392       code: /^( {4}[^\n]+\n*)+/,
18393       fences: noop,
18394       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18395       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18396       nptable: noop,
18397       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18398       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18399       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18400       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18401       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18402       table: noop,
18403       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18404       text: /^[^\n]+/
18405     };
18406     
18407     block.bullet = /(?:[*+-]|\d+\.)/;
18408     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18409     block.item = replace(block.item, 'gm')
18410       (/bull/g, block.bullet)
18411       ();
18412     
18413     block.list = replace(block.list)
18414       (/bull/g, block.bullet)
18415       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18416       ('def', '\\n+(?=' + block.def.source + ')')
18417       ();
18418     
18419     block.blockquote = replace(block.blockquote)
18420       ('def', block.def)
18421       ();
18422     
18423     block._tag = '(?!(?:'
18424       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18425       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18426       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18427     
18428     block.html = replace(block.html)
18429       ('comment', /<!--[\s\S]*?-->/)
18430       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18431       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18432       (/tag/g, block._tag)
18433       ();
18434     
18435     block.paragraph = replace(block.paragraph)
18436       ('hr', block.hr)
18437       ('heading', block.heading)
18438       ('lheading', block.lheading)
18439       ('blockquote', block.blockquote)
18440       ('tag', '<' + block._tag)
18441       ('def', block.def)
18442       ();
18443     
18444     /**
18445      * Normal Block Grammar
18446      */
18447     
18448     block.normal = merge({}, block);
18449     
18450     /**
18451      * GFM Block Grammar
18452      */
18453     
18454     block.gfm = merge({}, block.normal, {
18455       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18456       paragraph: /^/,
18457       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18458     });
18459     
18460     block.gfm.paragraph = replace(block.paragraph)
18461       ('(?!', '(?!'
18462         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18463         + block.list.source.replace('\\1', '\\3') + '|')
18464       ();
18465     
18466     /**
18467      * GFM + Tables Block Grammar
18468      */
18469     
18470     block.tables = merge({}, block.gfm, {
18471       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18472       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18473     });
18474     
18475     /**
18476      * Block Lexer
18477      */
18478     
18479     var Lexer = function (options) {
18480       this.tokens = [];
18481       this.tokens.links = {};
18482       this.options = options || marked.defaults;
18483       this.rules = block.normal;
18484     
18485       if (this.options.gfm) {
18486         if (this.options.tables) {
18487           this.rules = block.tables;
18488         } else {
18489           this.rules = block.gfm;
18490         }
18491       }
18492     }
18493     
18494     /**
18495      * Expose Block Rules
18496      */
18497     
18498     Lexer.rules = block;
18499     
18500     /**
18501      * Static Lex Method
18502      */
18503     
18504     Lexer.lex = function(src, options) {
18505       var lexer = new Lexer(options);
18506       return lexer.lex(src);
18507     };
18508     
18509     /**
18510      * Preprocessing
18511      */
18512     
18513     Lexer.prototype.lex = function(src) {
18514       src = src
18515         .replace(/\r\n|\r/g, '\n')
18516         .replace(/\t/g, '    ')
18517         .replace(/\u00a0/g, ' ')
18518         .replace(/\u2424/g, '\n');
18519     
18520       return this.token(src, true);
18521     };
18522     
18523     /**
18524      * Lexing
18525      */
18526     
18527     Lexer.prototype.token = function(src, top, bq) {
18528       var src = src.replace(/^ +$/gm, '')
18529         , next
18530         , loose
18531         , cap
18532         , bull
18533         , b
18534         , item
18535         , space
18536         , i
18537         , l;
18538     
18539       while (src) {
18540         // newline
18541         if (cap = this.rules.newline.exec(src)) {
18542           src = src.substring(cap[0].length);
18543           if (cap[0].length > 1) {
18544             this.tokens.push({
18545               type: 'space'
18546             });
18547           }
18548         }
18549     
18550         // code
18551         if (cap = this.rules.code.exec(src)) {
18552           src = src.substring(cap[0].length);
18553           cap = cap[0].replace(/^ {4}/gm, '');
18554           this.tokens.push({
18555             type: 'code',
18556             text: !this.options.pedantic
18557               ? cap.replace(/\n+$/, '')
18558               : cap
18559           });
18560           continue;
18561         }
18562     
18563         // fences (gfm)
18564         if (cap = this.rules.fences.exec(src)) {
18565           src = src.substring(cap[0].length);
18566           this.tokens.push({
18567             type: 'code',
18568             lang: cap[2],
18569             text: cap[3] || ''
18570           });
18571           continue;
18572         }
18573     
18574         // heading
18575         if (cap = this.rules.heading.exec(src)) {
18576           src = src.substring(cap[0].length);
18577           this.tokens.push({
18578             type: 'heading',
18579             depth: cap[1].length,
18580             text: cap[2]
18581           });
18582           continue;
18583         }
18584     
18585         // table no leading pipe (gfm)
18586         if (top && (cap = this.rules.nptable.exec(src))) {
18587           src = src.substring(cap[0].length);
18588     
18589           item = {
18590             type: 'table',
18591             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18592             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18593             cells: cap[3].replace(/\n$/, '').split('\n')
18594           };
18595     
18596           for (i = 0; i < item.align.length; i++) {
18597             if (/^ *-+: *$/.test(item.align[i])) {
18598               item.align[i] = 'right';
18599             } else if (/^ *:-+: *$/.test(item.align[i])) {
18600               item.align[i] = 'center';
18601             } else if (/^ *:-+ *$/.test(item.align[i])) {
18602               item.align[i] = 'left';
18603             } else {
18604               item.align[i] = null;
18605             }
18606           }
18607     
18608           for (i = 0; i < item.cells.length; i++) {
18609             item.cells[i] = item.cells[i].split(/ *\| */);
18610           }
18611     
18612           this.tokens.push(item);
18613     
18614           continue;
18615         }
18616     
18617         // lheading
18618         if (cap = this.rules.lheading.exec(src)) {
18619           src = src.substring(cap[0].length);
18620           this.tokens.push({
18621             type: 'heading',
18622             depth: cap[2] === '=' ? 1 : 2,
18623             text: cap[1]
18624           });
18625           continue;
18626         }
18627     
18628         // hr
18629         if (cap = this.rules.hr.exec(src)) {
18630           src = src.substring(cap[0].length);
18631           this.tokens.push({
18632             type: 'hr'
18633           });
18634           continue;
18635         }
18636     
18637         // blockquote
18638         if (cap = this.rules.blockquote.exec(src)) {
18639           src = src.substring(cap[0].length);
18640     
18641           this.tokens.push({
18642             type: 'blockquote_start'
18643           });
18644     
18645           cap = cap[0].replace(/^ *> ?/gm, '');
18646     
18647           // Pass `top` to keep the current
18648           // "toplevel" state. This is exactly
18649           // how markdown.pl works.
18650           this.token(cap, top, true);
18651     
18652           this.tokens.push({
18653             type: 'blockquote_end'
18654           });
18655     
18656           continue;
18657         }
18658     
18659         // list
18660         if (cap = this.rules.list.exec(src)) {
18661           src = src.substring(cap[0].length);
18662           bull = cap[2];
18663     
18664           this.tokens.push({
18665             type: 'list_start',
18666             ordered: bull.length > 1
18667           });
18668     
18669           // Get each top-level item.
18670           cap = cap[0].match(this.rules.item);
18671     
18672           next = false;
18673           l = cap.length;
18674           i = 0;
18675     
18676           for (; i < l; i++) {
18677             item = cap[i];
18678     
18679             // Remove the list item's bullet
18680             // so it is seen as the next token.
18681             space = item.length;
18682             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18683     
18684             // Outdent whatever the
18685             // list item contains. Hacky.
18686             if (~item.indexOf('\n ')) {
18687               space -= item.length;
18688               item = !this.options.pedantic
18689                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18690                 : item.replace(/^ {1,4}/gm, '');
18691             }
18692     
18693             // Determine whether the next list item belongs here.
18694             // Backpedal if it does not belong in this list.
18695             if (this.options.smartLists && i !== l - 1) {
18696               b = block.bullet.exec(cap[i + 1])[0];
18697               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18698                 src = cap.slice(i + 1).join('\n') + src;
18699                 i = l - 1;
18700               }
18701             }
18702     
18703             // Determine whether item is loose or not.
18704             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18705             // for discount behavior.
18706             loose = next || /\n\n(?!\s*$)/.test(item);
18707             if (i !== l - 1) {
18708               next = item.charAt(item.length - 1) === '\n';
18709               if (!loose) { loose = next; }
18710             }
18711     
18712             this.tokens.push({
18713               type: loose
18714                 ? 'loose_item_start'
18715                 : 'list_item_start'
18716             });
18717     
18718             // Recurse.
18719             this.token(item, false, bq);
18720     
18721             this.tokens.push({
18722               type: 'list_item_end'
18723             });
18724           }
18725     
18726           this.tokens.push({
18727             type: 'list_end'
18728           });
18729     
18730           continue;
18731         }
18732     
18733         // html
18734         if (cap = this.rules.html.exec(src)) {
18735           src = src.substring(cap[0].length);
18736           this.tokens.push({
18737             type: this.options.sanitize
18738               ? 'paragraph'
18739               : 'html',
18740             pre: !this.options.sanitizer
18741               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18742             text: cap[0]
18743           });
18744           continue;
18745         }
18746     
18747         // def
18748         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18749           src = src.substring(cap[0].length);
18750           this.tokens.links[cap[1].toLowerCase()] = {
18751             href: cap[2],
18752             title: cap[3]
18753           };
18754           continue;
18755         }
18756     
18757         // table (gfm)
18758         if (top && (cap = this.rules.table.exec(src))) {
18759           src = src.substring(cap[0].length);
18760     
18761           item = {
18762             type: 'table',
18763             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18764             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18765             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18766           };
18767     
18768           for (i = 0; i < item.align.length; i++) {
18769             if (/^ *-+: *$/.test(item.align[i])) {
18770               item.align[i] = 'right';
18771             } else if (/^ *:-+: *$/.test(item.align[i])) {
18772               item.align[i] = 'center';
18773             } else if (/^ *:-+ *$/.test(item.align[i])) {
18774               item.align[i] = 'left';
18775             } else {
18776               item.align[i] = null;
18777             }
18778           }
18779     
18780           for (i = 0; i < item.cells.length; i++) {
18781             item.cells[i] = item.cells[i]
18782               .replace(/^ *\| *| *\| *$/g, '')
18783               .split(/ *\| */);
18784           }
18785     
18786           this.tokens.push(item);
18787     
18788           continue;
18789         }
18790     
18791         // top-level paragraph
18792         if (top && (cap = this.rules.paragraph.exec(src))) {
18793           src = src.substring(cap[0].length);
18794           this.tokens.push({
18795             type: 'paragraph',
18796             text: cap[1].charAt(cap[1].length - 1) === '\n'
18797               ? cap[1].slice(0, -1)
18798               : cap[1]
18799           });
18800           continue;
18801         }
18802     
18803         // text
18804         if (cap = this.rules.text.exec(src)) {
18805           // Top-level should never reach here.
18806           src = src.substring(cap[0].length);
18807           this.tokens.push({
18808             type: 'text',
18809             text: cap[0]
18810           });
18811           continue;
18812         }
18813     
18814         if (src) {
18815           throw new
18816             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18817         }
18818       }
18819     
18820       return this.tokens;
18821     };
18822     
18823     /**
18824      * Inline-Level Grammar
18825      */
18826     
18827     var inline = {
18828       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18829       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18830       url: noop,
18831       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18832       link: /^!?\[(inside)\]\(href\)/,
18833       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18834       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18835       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18836       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18837       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18838       br: /^ {2,}\n(?!\s*$)/,
18839       del: noop,
18840       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18841     };
18842     
18843     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18844     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18845     
18846     inline.link = replace(inline.link)
18847       ('inside', inline._inside)
18848       ('href', inline._href)
18849       ();
18850     
18851     inline.reflink = replace(inline.reflink)
18852       ('inside', inline._inside)
18853       ();
18854     
18855     /**
18856      * Normal Inline Grammar
18857      */
18858     
18859     inline.normal = merge({}, inline);
18860     
18861     /**
18862      * Pedantic Inline Grammar
18863      */
18864     
18865     inline.pedantic = merge({}, inline.normal, {
18866       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18867       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18868     });
18869     
18870     /**
18871      * GFM Inline Grammar
18872      */
18873     
18874     inline.gfm = merge({}, inline.normal, {
18875       escape: replace(inline.escape)('])', '~|])')(),
18876       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18877       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18878       text: replace(inline.text)
18879         (']|', '~]|')
18880         ('|', '|https?://|')
18881         ()
18882     });
18883     
18884     /**
18885      * GFM + Line Breaks Inline Grammar
18886      */
18887     
18888     inline.breaks = merge({}, inline.gfm, {
18889       br: replace(inline.br)('{2,}', '*')(),
18890       text: replace(inline.gfm.text)('{2,}', '*')()
18891     });
18892     
18893     /**
18894      * Inline Lexer & Compiler
18895      */
18896     
18897     var InlineLexer  = function (links, options) {
18898       this.options = options || marked.defaults;
18899       this.links = links;
18900       this.rules = inline.normal;
18901       this.renderer = this.options.renderer || new Renderer;
18902       this.renderer.options = this.options;
18903     
18904       if (!this.links) {
18905         throw new
18906           Error('Tokens array requires a `links` property.');
18907       }
18908     
18909       if (this.options.gfm) {
18910         if (this.options.breaks) {
18911           this.rules = inline.breaks;
18912         } else {
18913           this.rules = inline.gfm;
18914         }
18915       } else if (this.options.pedantic) {
18916         this.rules = inline.pedantic;
18917       }
18918     }
18919     
18920     /**
18921      * Expose Inline Rules
18922      */
18923     
18924     InlineLexer.rules = inline;
18925     
18926     /**
18927      * Static Lexing/Compiling Method
18928      */
18929     
18930     InlineLexer.output = function(src, links, options) {
18931       var inline = new InlineLexer(links, options);
18932       return inline.output(src);
18933     };
18934     
18935     /**
18936      * Lexing/Compiling
18937      */
18938     
18939     InlineLexer.prototype.output = function(src) {
18940       var out = ''
18941         , link
18942         , text
18943         , href
18944         , cap;
18945     
18946       while (src) {
18947         // escape
18948         if (cap = this.rules.escape.exec(src)) {
18949           src = src.substring(cap[0].length);
18950           out += cap[1];
18951           continue;
18952         }
18953     
18954         // autolink
18955         if (cap = this.rules.autolink.exec(src)) {
18956           src = src.substring(cap[0].length);
18957           if (cap[2] === '@') {
18958             text = cap[1].charAt(6) === ':'
18959               ? this.mangle(cap[1].substring(7))
18960               : this.mangle(cap[1]);
18961             href = this.mangle('mailto:') + text;
18962           } else {
18963             text = escape(cap[1]);
18964             href = text;
18965           }
18966           out += this.renderer.link(href, null, text);
18967           continue;
18968         }
18969     
18970         // url (gfm)
18971         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18972           src = src.substring(cap[0].length);
18973           text = escape(cap[1]);
18974           href = text;
18975           out += this.renderer.link(href, null, text);
18976           continue;
18977         }
18978     
18979         // tag
18980         if (cap = this.rules.tag.exec(src)) {
18981           if (!this.inLink && /^<a /i.test(cap[0])) {
18982             this.inLink = true;
18983           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18984             this.inLink = false;
18985           }
18986           src = src.substring(cap[0].length);
18987           out += this.options.sanitize
18988             ? this.options.sanitizer
18989               ? this.options.sanitizer(cap[0])
18990               : escape(cap[0])
18991             : cap[0];
18992           continue;
18993         }
18994     
18995         // link
18996         if (cap = this.rules.link.exec(src)) {
18997           src = src.substring(cap[0].length);
18998           this.inLink = true;
18999           out += this.outputLink(cap, {
19000             href: cap[2],
19001             title: cap[3]
19002           });
19003           this.inLink = false;
19004           continue;
19005         }
19006     
19007         // reflink, nolink
19008         if ((cap = this.rules.reflink.exec(src))
19009             || (cap = this.rules.nolink.exec(src))) {
19010           src = src.substring(cap[0].length);
19011           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19012           link = this.links[link.toLowerCase()];
19013           if (!link || !link.href) {
19014             out += cap[0].charAt(0);
19015             src = cap[0].substring(1) + src;
19016             continue;
19017           }
19018           this.inLink = true;
19019           out += this.outputLink(cap, link);
19020           this.inLink = false;
19021           continue;
19022         }
19023     
19024         // strong
19025         if (cap = this.rules.strong.exec(src)) {
19026           src = src.substring(cap[0].length);
19027           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19028           continue;
19029         }
19030     
19031         // em
19032         if (cap = this.rules.em.exec(src)) {
19033           src = src.substring(cap[0].length);
19034           out += this.renderer.em(this.output(cap[2] || cap[1]));
19035           continue;
19036         }
19037     
19038         // code
19039         if (cap = this.rules.code.exec(src)) {
19040           src = src.substring(cap[0].length);
19041           out += this.renderer.codespan(escape(cap[2], true));
19042           continue;
19043         }
19044     
19045         // br
19046         if (cap = this.rules.br.exec(src)) {
19047           src = src.substring(cap[0].length);
19048           out += this.renderer.br();
19049           continue;
19050         }
19051     
19052         // del (gfm)
19053         if (cap = this.rules.del.exec(src)) {
19054           src = src.substring(cap[0].length);
19055           out += this.renderer.del(this.output(cap[1]));
19056           continue;
19057         }
19058     
19059         // text
19060         if (cap = this.rules.text.exec(src)) {
19061           src = src.substring(cap[0].length);
19062           out += this.renderer.text(escape(this.smartypants(cap[0])));
19063           continue;
19064         }
19065     
19066         if (src) {
19067           throw new
19068             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19069         }
19070       }
19071     
19072       return out;
19073     };
19074     
19075     /**
19076      * Compile Link
19077      */
19078     
19079     InlineLexer.prototype.outputLink = function(cap, link) {
19080       var href = escape(link.href)
19081         , title = link.title ? escape(link.title) : null;
19082     
19083       return cap[0].charAt(0) !== '!'
19084         ? this.renderer.link(href, title, this.output(cap[1]))
19085         : this.renderer.image(href, title, escape(cap[1]));
19086     };
19087     
19088     /**
19089      * Smartypants Transformations
19090      */
19091     
19092     InlineLexer.prototype.smartypants = function(text) {
19093       if (!this.options.smartypants)  { return text; }
19094       return text
19095         // em-dashes
19096         .replace(/---/g, '\u2014')
19097         // en-dashes
19098         .replace(/--/g, '\u2013')
19099         // opening singles
19100         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19101         // closing singles & apostrophes
19102         .replace(/'/g, '\u2019')
19103         // opening doubles
19104         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19105         // closing doubles
19106         .replace(/"/g, '\u201d')
19107         // ellipses
19108         .replace(/\.{3}/g, '\u2026');
19109     };
19110     
19111     /**
19112      * Mangle Links
19113      */
19114     
19115     InlineLexer.prototype.mangle = function(text) {
19116       if (!this.options.mangle) { return text; }
19117       var out = ''
19118         , l = text.length
19119         , i = 0
19120         , ch;
19121     
19122       for (; i < l; i++) {
19123         ch = text.charCodeAt(i);
19124         if (Math.random() > 0.5) {
19125           ch = 'x' + ch.toString(16);
19126         }
19127         out += '&#' + ch + ';';
19128       }
19129     
19130       return out;
19131     };
19132     
19133     /**
19134      * Renderer
19135      */
19136     
19137      /**
19138          * eval:var:Renderer
19139     */
19140     
19141     var Renderer   = function (options) {
19142       this.options = options || {};
19143     }
19144     
19145     Renderer.prototype.code = function(code, lang, escaped) {
19146       if (this.options.highlight) {
19147         var out = this.options.highlight(code, lang);
19148         if (out != null && out !== code) {
19149           escaped = true;
19150           code = out;
19151         }
19152       } else {
19153             // hack!!! - it's already escapeD?
19154             escaped = true;
19155       }
19156     
19157       if (!lang) {
19158         return '<pre><code>'
19159           + (escaped ? code : escape(code, true))
19160           + '\n</code></pre>';
19161       }
19162     
19163       return '<pre><code class="'
19164         + this.options.langPrefix
19165         + escape(lang, true)
19166         + '">'
19167         + (escaped ? code : escape(code, true))
19168         + '\n</code></pre>\n';
19169     };
19170     
19171     Renderer.prototype.blockquote = function(quote) {
19172       return '<blockquote>\n' + quote + '</blockquote>\n';
19173     };
19174     
19175     Renderer.prototype.html = function(html) {
19176       return html;
19177     };
19178     
19179     Renderer.prototype.heading = function(text, level, raw) {
19180       return '<h'
19181         + level
19182         + ' id="'
19183         + this.options.headerPrefix
19184         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19185         + '">'
19186         + text
19187         + '</h'
19188         + level
19189         + '>\n';
19190     };
19191     
19192     Renderer.prototype.hr = function() {
19193       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19194     };
19195     
19196     Renderer.prototype.list = function(body, ordered) {
19197       var type = ordered ? 'ol' : 'ul';
19198       return '<' + type + '>\n' + body + '</' + type + '>\n';
19199     };
19200     
19201     Renderer.prototype.listitem = function(text) {
19202       return '<li>' + text + '</li>\n';
19203     };
19204     
19205     Renderer.prototype.paragraph = function(text) {
19206       return '<p>' + text + '</p>\n';
19207     };
19208     
19209     Renderer.prototype.table = function(header, body) {
19210       return '<table class="table table-striped">\n'
19211         + '<thead>\n'
19212         + header
19213         + '</thead>\n'
19214         + '<tbody>\n'
19215         + body
19216         + '</tbody>\n'
19217         + '</table>\n';
19218     };
19219     
19220     Renderer.prototype.tablerow = function(content) {
19221       return '<tr>\n' + content + '</tr>\n';
19222     };
19223     
19224     Renderer.prototype.tablecell = function(content, flags) {
19225       var type = flags.header ? 'th' : 'td';
19226       var tag = flags.align
19227         ? '<' + type + ' style="text-align:' + flags.align + '">'
19228         : '<' + type + '>';
19229       return tag + content + '</' + type + '>\n';
19230     };
19231     
19232     // span level renderer
19233     Renderer.prototype.strong = function(text) {
19234       return '<strong>' + text + '</strong>';
19235     };
19236     
19237     Renderer.prototype.em = function(text) {
19238       return '<em>' + text + '</em>';
19239     };
19240     
19241     Renderer.prototype.codespan = function(text) {
19242       return '<code>' + text + '</code>';
19243     };
19244     
19245     Renderer.prototype.br = function() {
19246       return this.options.xhtml ? '<br/>' : '<br>';
19247     };
19248     
19249     Renderer.prototype.del = function(text) {
19250       return '<del>' + text + '</del>';
19251     };
19252     
19253     Renderer.prototype.link = function(href, title, text) {
19254       if (this.options.sanitize) {
19255         try {
19256           var prot = decodeURIComponent(unescape(href))
19257             .replace(/[^\w:]/g, '')
19258             .toLowerCase();
19259         } catch (e) {
19260           return '';
19261         }
19262         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19263           return '';
19264         }
19265       }
19266       var out = '<a href="' + href + '"';
19267       if (title) {
19268         out += ' title="' + title + '"';
19269       }
19270       out += '>' + text + '</a>';
19271       return out;
19272     };
19273     
19274     Renderer.prototype.image = function(href, title, text) {
19275       var out = '<img src="' + href + '" alt="' + text + '"';
19276       if (title) {
19277         out += ' title="' + title + '"';
19278       }
19279       out += this.options.xhtml ? '/>' : '>';
19280       return out;
19281     };
19282     
19283     Renderer.prototype.text = function(text) {
19284       return text;
19285     };
19286     
19287     /**
19288      * Parsing & Compiling
19289      */
19290          /**
19291          * eval:var:Parser
19292     */
19293     
19294     var Parser= function (options) {
19295       this.tokens = [];
19296       this.token = null;
19297       this.options = options || marked.defaults;
19298       this.options.renderer = this.options.renderer || new Renderer;
19299       this.renderer = this.options.renderer;
19300       this.renderer.options = this.options;
19301     }
19302     
19303     /**
19304      * Static Parse Method
19305      */
19306     
19307     Parser.parse = function(src, options, renderer) {
19308       var parser = new Parser(options, renderer);
19309       return parser.parse(src);
19310     };
19311     
19312     /**
19313      * Parse Loop
19314      */
19315     
19316     Parser.prototype.parse = function(src) {
19317       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19318       this.tokens = src.reverse();
19319     
19320       var out = '';
19321       while (this.next()) {
19322         out += this.tok();
19323       }
19324     
19325       return out;
19326     };
19327     
19328     /**
19329      * Next Token
19330      */
19331     
19332     Parser.prototype.next = function() {
19333       return this.token = this.tokens.pop();
19334     };
19335     
19336     /**
19337      * Preview Next Token
19338      */
19339     
19340     Parser.prototype.peek = function() {
19341       return this.tokens[this.tokens.length - 1] || 0;
19342     };
19343     
19344     /**
19345      * Parse Text Tokens
19346      */
19347     
19348     Parser.prototype.parseText = function() {
19349       var body = this.token.text;
19350     
19351       while (this.peek().type === 'text') {
19352         body += '\n' + this.next().text;
19353       }
19354     
19355       return this.inline.output(body);
19356     };
19357     
19358     /**
19359      * Parse Current Token
19360      */
19361     
19362     Parser.prototype.tok = function() {
19363       switch (this.token.type) {
19364         case 'space': {
19365           return '';
19366         }
19367         case 'hr': {
19368           return this.renderer.hr();
19369         }
19370         case 'heading': {
19371           return this.renderer.heading(
19372             this.inline.output(this.token.text),
19373             this.token.depth,
19374             this.token.text);
19375         }
19376         case 'code': {
19377           return this.renderer.code(this.token.text,
19378             this.token.lang,
19379             this.token.escaped);
19380         }
19381         case 'table': {
19382           var header = ''
19383             , body = ''
19384             , i
19385             , row
19386             , cell
19387             , flags
19388             , j;
19389     
19390           // header
19391           cell = '';
19392           for (i = 0; i < this.token.header.length; i++) {
19393             flags = { header: true, align: this.token.align[i] };
19394             cell += this.renderer.tablecell(
19395               this.inline.output(this.token.header[i]),
19396               { header: true, align: this.token.align[i] }
19397             );
19398           }
19399           header += this.renderer.tablerow(cell);
19400     
19401           for (i = 0; i < this.token.cells.length; i++) {
19402             row = this.token.cells[i];
19403     
19404             cell = '';
19405             for (j = 0; j < row.length; j++) {
19406               cell += this.renderer.tablecell(
19407                 this.inline.output(row[j]),
19408                 { header: false, align: this.token.align[j] }
19409               );
19410             }
19411     
19412             body += this.renderer.tablerow(cell);
19413           }
19414           return this.renderer.table(header, body);
19415         }
19416         case 'blockquote_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'blockquote_end') {
19420             body += this.tok();
19421           }
19422     
19423           return this.renderer.blockquote(body);
19424         }
19425         case 'list_start': {
19426           var body = ''
19427             , ordered = this.token.ordered;
19428     
19429           while (this.next().type !== 'list_end') {
19430             body += this.tok();
19431           }
19432     
19433           return this.renderer.list(body, ordered);
19434         }
19435         case 'list_item_start': {
19436           var body = '';
19437     
19438           while (this.next().type !== 'list_item_end') {
19439             body += this.token.type === 'text'
19440               ? this.parseText()
19441               : this.tok();
19442           }
19443     
19444           return this.renderer.listitem(body);
19445         }
19446         case 'loose_item_start': {
19447           var body = '';
19448     
19449           while (this.next().type !== 'list_item_end') {
19450             body += this.tok();
19451           }
19452     
19453           return this.renderer.listitem(body);
19454         }
19455         case 'html': {
19456           var html = !this.token.pre && !this.options.pedantic
19457             ? this.inline.output(this.token.text)
19458             : this.token.text;
19459           return this.renderer.html(html);
19460         }
19461         case 'paragraph': {
19462           return this.renderer.paragraph(this.inline.output(this.token.text));
19463         }
19464         case 'text': {
19465           return this.renderer.paragraph(this.parseText());
19466         }
19467       }
19468     };
19469   
19470     
19471     /**
19472      * Marked
19473      */
19474          /**
19475          * eval:var:marked
19476     */
19477     var marked = function (src, opt, callback) {
19478       if (callback || typeof opt === 'function') {
19479         if (!callback) {
19480           callback = opt;
19481           opt = null;
19482         }
19483     
19484         opt = merge({}, marked.defaults, opt || {});
19485     
19486         var highlight = opt.highlight
19487           , tokens
19488           , pending
19489           , i = 0;
19490     
19491         try {
19492           tokens = Lexer.lex(src, opt)
19493         } catch (e) {
19494           return callback(e);
19495         }
19496     
19497         pending = tokens.length;
19498          /**
19499          * eval:var:done
19500     */
19501         var done = function(err) {
19502           if (err) {
19503             opt.highlight = highlight;
19504             return callback(err);
19505           }
19506     
19507           var out;
19508     
19509           try {
19510             out = Parser.parse(tokens, opt);
19511           } catch (e) {
19512             err = e;
19513           }
19514     
19515           opt.highlight = highlight;
19516     
19517           return err
19518             ? callback(err)
19519             : callback(null, out);
19520         };
19521     
19522         if (!highlight || highlight.length < 3) {
19523           return done();
19524         }
19525     
19526         delete opt.highlight;
19527     
19528         if (!pending) { return done(); }
19529     
19530         for (; i < tokens.length; i++) {
19531           (function(token) {
19532             if (token.type !== 'code') {
19533               return --pending || done();
19534             }
19535             return highlight(token.text, token.lang, function(err, code) {
19536               if (err) { return done(err); }
19537               if (code == null || code === token.text) {
19538                 return --pending || done();
19539               }
19540               token.text = code;
19541               token.escaped = true;
19542               --pending || done();
19543             });
19544           })(tokens[i]);
19545         }
19546     
19547         return;
19548       }
19549       try {
19550         if (opt) { opt = merge({}, marked.defaults, opt); }
19551         return Parser.parse(Lexer.lex(src, opt), opt);
19552       } catch (e) {
19553         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19554         if ((opt || marked.defaults).silent) {
19555           return '<p>An error occured:</p><pre>'
19556             + escape(e.message + '', true)
19557             + '</pre>';
19558         }
19559         throw e;
19560       }
19561     }
19562     
19563     /**
19564      * Options
19565      */
19566     
19567     marked.options =
19568     marked.setOptions = function(opt) {
19569       merge(marked.defaults, opt);
19570       return marked;
19571     };
19572     
19573     marked.defaults = {
19574       gfm: true,
19575       tables: true,
19576       breaks: false,
19577       pedantic: false,
19578       sanitize: false,
19579       sanitizer: null,
19580       mangle: true,
19581       smartLists: false,
19582       silent: false,
19583       highlight: null,
19584       langPrefix: 'lang-',
19585       smartypants: false,
19586       headerPrefix: '',
19587       renderer: new Renderer,
19588       xhtml: false
19589     };
19590     
19591     /**
19592      * Expose
19593      */
19594     
19595     marked.Parser = Parser;
19596     marked.parser = Parser.parse;
19597     
19598     marked.Renderer = Renderer;
19599     
19600     marked.Lexer = Lexer;
19601     marked.lexer = Lexer.lex;
19602     
19603     marked.InlineLexer = InlineLexer;
19604     marked.inlineLexer = InlineLexer.output;
19605     
19606     marked.parse = marked;
19607     
19608     Roo.Markdown.marked = marked;
19609
19610 })();/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620
19621
19622
19623 /*
19624  * These classes are derivatives of the similarly named classes in the YUI Library.
19625  * The original license:
19626  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19627  * Code licensed under the BSD License:
19628  * http://developer.yahoo.net/yui/license.txt
19629  */
19630
19631 (function() {
19632
19633 var Event=Roo.EventManager;
19634 var Dom=Roo.lib.Dom;
19635
19636 /**
19637  * @class Roo.dd.DragDrop
19638  * @extends Roo.util.Observable
19639  * Defines the interface and base operation of items that that can be
19640  * dragged or can be drop targets.  It was designed to be extended, overriding
19641  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19642  * Up to three html elements can be associated with a DragDrop instance:
19643  * <ul>
19644  * <li>linked element: the element that is passed into the constructor.
19645  * This is the element which defines the boundaries for interaction with
19646  * other DragDrop objects.</li>
19647  * <li>handle element(s): The drag operation only occurs if the element that
19648  * was clicked matches a handle element.  By default this is the linked
19649  * element, but there are times that you will want only a portion of the
19650  * linked element to initiate the drag operation, and the setHandleElId()
19651  * method provides a way to define this.</li>
19652  * <li>drag element: this represents the element that would be moved along
19653  * with the cursor during a drag operation.  By default, this is the linked
19654  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19655  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19656  * </li>
19657  * </ul>
19658  * This class should not be instantiated until the onload event to ensure that
19659  * the associated elements are available.
19660  * The following would define a DragDrop obj that would interact with any
19661  * other DragDrop obj in the "group1" group:
19662  * <pre>
19663  *  dd = new Roo.dd.DragDrop("div1", "group1");
19664  * </pre>
19665  * Since none of the event handlers have been implemented, nothing would
19666  * actually happen if you were to run the code above.  Normally you would
19667  * override this class or one of the default implementations, but you can
19668  * also override the methods you want on an instance of the class...
19669  * <pre>
19670  *  dd.onDragDrop = function(e, id) {
19671  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19672  *  }
19673  * </pre>
19674  * @constructor
19675  * @param {String} id of the element that is linked to this instance
19676  * @param {String} sGroup the group of related DragDrop objects
19677  * @param {object} config an object containing configurable attributes
19678  *                Valid properties for DragDrop:
19679  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19680  */
19681 Roo.dd.DragDrop = function(id, sGroup, config) {
19682     if (id) {
19683         this.init(id, sGroup, config);
19684     }
19685     
19686 };
19687
19688 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19689
19690     /**
19691      * The id of the element associated with this object.  This is what we
19692      * refer to as the "linked element" because the size and position of
19693      * this element is used to determine when the drag and drop objects have
19694      * interacted.
19695      * @property id
19696      * @type String
19697      */
19698     id: null,
19699
19700     /**
19701      * Configuration attributes passed into the constructor
19702      * @property config
19703      * @type object
19704      */
19705     config: null,
19706
19707     /**
19708      * The id of the element that will be dragged.  By default this is same
19709      * as the linked element , but could be changed to another element. Ex:
19710      * Roo.dd.DDProxy
19711      * @property dragElId
19712      * @type String
19713      * @private
19714      */
19715     dragElId: null,
19716
19717     /**
19718      * the id of the element that initiates the drag operation.  By default
19719      * this is the linked element, but could be changed to be a child of this
19720      * element.  This lets us do things like only starting the drag when the
19721      * header element within the linked html element is clicked.
19722      * @property handleElId
19723      * @type String
19724      * @private
19725      */
19726     handleElId: null,
19727
19728     /**
19729      * An associative array of HTML tags that will be ignored if clicked.
19730      * @property invalidHandleTypes
19731      * @type {string: string}
19732      */
19733     invalidHandleTypes: null,
19734
19735     /**
19736      * An associative array of ids for elements that will be ignored if clicked
19737      * @property invalidHandleIds
19738      * @type {string: string}
19739      */
19740     invalidHandleIds: null,
19741
19742     /**
19743      * An indexted array of css class names for elements that will be ignored
19744      * if clicked.
19745      * @property invalidHandleClasses
19746      * @type string[]
19747      */
19748     invalidHandleClasses: null,
19749
19750     /**
19751      * The linked element's absolute X position at the time the drag was
19752      * started
19753      * @property startPageX
19754      * @type int
19755      * @private
19756      */
19757     startPageX: 0,
19758
19759     /**
19760      * The linked element's absolute X position at the time the drag was
19761      * started
19762      * @property startPageY
19763      * @type int
19764      * @private
19765      */
19766     startPageY: 0,
19767
19768     /**
19769      * The group defines a logical collection of DragDrop objects that are
19770      * related.  Instances only get events when interacting with other
19771      * DragDrop object in the same group.  This lets us define multiple
19772      * groups using a single DragDrop subclass if we want.
19773      * @property groups
19774      * @type {string: string}
19775      */
19776     groups: null,
19777
19778     /**
19779      * Individual drag/drop instances can be locked.  This will prevent
19780      * onmousedown start drag.
19781      * @property locked
19782      * @type boolean
19783      * @private
19784      */
19785     locked: false,
19786
19787     /**
19788      * Lock this instance
19789      * @method lock
19790      */
19791     lock: function() { this.locked = true; },
19792
19793     /**
19794      * Unlock this instace
19795      * @method unlock
19796      */
19797     unlock: function() { this.locked = false; },
19798
19799     /**
19800      * By default, all insances can be a drop target.  This can be disabled by
19801      * setting isTarget to false.
19802      * @method isTarget
19803      * @type boolean
19804      */
19805     isTarget: true,
19806
19807     /**
19808      * The padding configured for this drag and drop object for calculating
19809      * the drop zone intersection with this object.
19810      * @method padding
19811      * @type int[]
19812      */
19813     padding: null,
19814
19815     /**
19816      * Cached reference to the linked element
19817      * @property _domRef
19818      * @private
19819      */
19820     _domRef: null,
19821
19822     /**
19823      * Internal typeof flag
19824      * @property __ygDragDrop
19825      * @private
19826      */
19827     __ygDragDrop: true,
19828
19829     /**
19830      * Set to true when horizontal contraints are applied
19831      * @property constrainX
19832      * @type boolean
19833      * @private
19834      */
19835     constrainX: false,
19836
19837     /**
19838      * Set to true when vertical contraints are applied
19839      * @property constrainY
19840      * @type boolean
19841      * @private
19842      */
19843     constrainY: false,
19844
19845     /**
19846      * The left constraint
19847      * @property minX
19848      * @type int
19849      * @private
19850      */
19851     minX: 0,
19852
19853     /**
19854      * The right constraint
19855      * @property maxX
19856      * @type int
19857      * @private
19858      */
19859     maxX: 0,
19860
19861     /**
19862      * The up constraint
19863      * @property minY
19864      * @type int
19865      * @type int
19866      * @private
19867      */
19868     minY: 0,
19869
19870     /**
19871      * The down constraint
19872      * @property maxY
19873      * @type int
19874      * @private
19875      */
19876     maxY: 0,
19877
19878     /**
19879      * Maintain offsets when we resetconstraints.  Set to true when you want
19880      * the position of the element relative to its parent to stay the same
19881      * when the page changes
19882      *
19883      * @property maintainOffset
19884      * @type boolean
19885      */
19886     maintainOffset: false,
19887
19888     /**
19889      * Array of pixel locations the element will snap to if we specified a
19890      * horizontal graduation/interval.  This array is generated automatically
19891      * when you define a tick interval.
19892      * @property xTicks
19893      * @type int[]
19894      */
19895     xTicks: null,
19896
19897     /**
19898      * Array of pixel locations the element will snap to if we specified a
19899      * vertical graduation/interval.  This array is generated automatically
19900      * when you define a tick interval.
19901      * @property yTicks
19902      * @type int[]
19903      */
19904     yTicks: null,
19905
19906     /**
19907      * By default the drag and drop instance will only respond to the primary
19908      * button click (left button for a right-handed mouse).  Set to true to
19909      * allow drag and drop to start with any mouse click that is propogated
19910      * by the browser
19911      * @property primaryButtonOnly
19912      * @type boolean
19913      */
19914     primaryButtonOnly: true,
19915
19916     /**
19917      * The availabe property is false until the linked dom element is accessible.
19918      * @property available
19919      * @type boolean
19920      */
19921     available: false,
19922
19923     /**
19924      * By default, drags can only be initiated if the mousedown occurs in the
19925      * region the linked element is.  This is done in part to work around a
19926      * bug in some browsers that mis-report the mousedown if the previous
19927      * mouseup happened outside of the window.  This property is set to true
19928      * if outer handles are defined.
19929      *
19930      * @property hasOuterHandles
19931      * @type boolean
19932      * @default false
19933      */
19934     hasOuterHandles: false,
19935
19936     /**
19937      * Code that executes immediately before the startDrag event
19938      * @method b4StartDrag
19939      * @private
19940      */
19941     b4StartDrag: function(x, y) { },
19942
19943     /**
19944      * Abstract method called after a drag/drop object is clicked
19945      * and the drag or mousedown time thresholds have beeen met.
19946      * @method startDrag
19947      * @param {int} X click location
19948      * @param {int} Y click location
19949      */
19950     startDrag: function(x, y) { /* override this */ },
19951
19952     /**
19953      * Code that executes immediately before the onDrag event
19954      * @method b4Drag
19955      * @private
19956      */
19957     b4Drag: function(e) { },
19958
19959     /**
19960      * Abstract method called during the onMouseMove event while dragging an
19961      * object.
19962      * @method onDrag
19963      * @param {Event} e the mousemove event
19964      */
19965     onDrag: function(e) { /* override this */ },
19966
19967     /**
19968      * Abstract method called when this element fist begins hovering over
19969      * another DragDrop obj
19970      * @method onDragEnter
19971      * @param {Event} e the mousemove event
19972      * @param {String|DragDrop[]} id In POINT mode, the element
19973      * id this is hovering over.  In INTERSECT mode, an array of one or more
19974      * dragdrop items being hovered over.
19975      */
19976     onDragEnter: function(e, id) { /* override this */ },
19977
19978     /**
19979      * Code that executes immediately before the onDragOver event
19980      * @method b4DragOver
19981      * @private
19982      */
19983     b4DragOver: function(e) { },
19984
19985     /**
19986      * Abstract method called when this element is hovering over another
19987      * DragDrop obj
19988      * @method onDragOver
19989      * @param {Event} e the mousemove event
19990      * @param {String|DragDrop[]} id In POINT mode, the element
19991      * id this is hovering over.  In INTERSECT mode, an array of dd items
19992      * being hovered over.
19993      */
19994     onDragOver: function(e, id) { /* override this */ },
19995
19996     /**
19997      * Code that executes immediately before the onDragOut event
19998      * @method b4DragOut
19999      * @private
20000      */
20001     b4DragOut: function(e) { },
20002
20003     /**
20004      * Abstract method called when we are no longer hovering over an element
20005      * @method onDragOut
20006      * @param {Event} e the mousemove event
20007      * @param {String|DragDrop[]} id In POINT mode, the element
20008      * id this was hovering over.  In INTERSECT mode, an array of dd items
20009      * that the mouse is no longer over.
20010      */
20011     onDragOut: function(e, id) { /* override this */ },
20012
20013     /**
20014      * Code that executes immediately before the onDragDrop event
20015      * @method b4DragDrop
20016      * @private
20017      */
20018     b4DragDrop: function(e) { },
20019
20020     /**
20021      * Abstract method called when this item is dropped on another DragDrop
20022      * obj
20023      * @method onDragDrop
20024      * @param {Event} e the mouseup event
20025      * @param {String|DragDrop[]} id In POINT mode, the element
20026      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20027      * was dropped on.
20028      */
20029     onDragDrop: function(e, id) { /* override this */ },
20030
20031     /**
20032      * Abstract method called when this item is dropped on an area with no
20033      * drop target
20034      * @method onInvalidDrop
20035      * @param {Event} e the mouseup event
20036      */
20037     onInvalidDrop: function(e) { /* override this */ },
20038
20039     /**
20040      * Code that executes immediately before the endDrag event
20041      * @method b4EndDrag
20042      * @private
20043      */
20044     b4EndDrag: function(e) { },
20045
20046     /**
20047      * Fired when we are done dragging the object
20048      * @method endDrag
20049      * @param {Event} e the mouseup event
20050      */
20051     endDrag: function(e) { /* override this */ },
20052
20053     /**
20054      * Code executed immediately before the onMouseDown event
20055      * @method b4MouseDown
20056      * @param {Event} e the mousedown event
20057      * @private
20058      */
20059     b4MouseDown: function(e) {  },
20060
20061     /**
20062      * Event handler that fires when a drag/drop obj gets a mousedown
20063      * @method onMouseDown
20064      * @param {Event} e the mousedown event
20065      */
20066     onMouseDown: function(e) { /* override this */ },
20067
20068     /**
20069      * Event handler that fires when a drag/drop obj gets a mouseup
20070      * @method onMouseUp
20071      * @param {Event} e the mouseup event
20072      */
20073     onMouseUp: function(e) { /* override this */ },
20074
20075     /**
20076      * Override the onAvailable method to do what is needed after the initial
20077      * position was determined.
20078      * @method onAvailable
20079      */
20080     onAvailable: function () {
20081     },
20082
20083     /*
20084      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20085      * @type Object
20086      */
20087     defaultPadding : {left:0, right:0, top:0, bottom:0},
20088
20089     /*
20090      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20091  *
20092  * Usage:
20093  <pre><code>
20094  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20095                 { dragElId: "existingProxyDiv" });
20096  dd.startDrag = function(){
20097      this.constrainTo("parent-id");
20098  };
20099  </code></pre>
20100  * Or you can initalize it using the {@link Roo.Element} object:
20101  <pre><code>
20102  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20103      startDrag : function(){
20104          this.constrainTo("parent-id");
20105      }
20106  });
20107  </code></pre>
20108      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20109      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20110      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20111      * an object containing the sides to pad. For example: {right:10, bottom:10}
20112      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20113      */
20114     constrainTo : function(constrainTo, pad, inContent){
20115         if(typeof pad == "number"){
20116             pad = {left: pad, right:pad, top:pad, bottom:pad};
20117         }
20118         pad = pad || this.defaultPadding;
20119         var b = Roo.get(this.getEl()).getBox();
20120         var ce = Roo.get(constrainTo);
20121         var s = ce.getScroll();
20122         var c, cd = ce.dom;
20123         if(cd == document.body){
20124             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20125         }else{
20126             xy = ce.getXY();
20127             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20128         }
20129
20130
20131         var topSpace = b.y - c.y;
20132         var leftSpace = b.x - c.x;
20133
20134         this.resetConstraints();
20135         this.setXConstraint(leftSpace - (pad.left||0), // left
20136                 c.width - leftSpace - b.width - (pad.right||0) //right
20137         );
20138         this.setYConstraint(topSpace - (pad.top||0), //top
20139                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20140         );
20141     },
20142
20143     /**
20144      * Returns a reference to the linked element
20145      * @method getEl
20146      * @return {HTMLElement} the html element
20147      */
20148     getEl: function() {
20149         if (!this._domRef) {
20150             this._domRef = Roo.getDom(this.id);
20151         }
20152
20153         return this._domRef;
20154     },
20155
20156     /**
20157      * Returns a reference to the actual element to drag.  By default this is
20158      * the same as the html element, but it can be assigned to another
20159      * element. An example of this can be found in Roo.dd.DDProxy
20160      * @method getDragEl
20161      * @return {HTMLElement} the html element
20162      */
20163     getDragEl: function() {
20164         return Roo.getDom(this.dragElId);
20165     },
20166
20167     /**
20168      * Sets up the DragDrop object.  Must be called in the constructor of any
20169      * Roo.dd.DragDrop subclass
20170      * @method init
20171      * @param id the id of the linked element
20172      * @param {String} sGroup the group of related items
20173      * @param {object} config configuration attributes
20174      */
20175     init: function(id, sGroup, config) {
20176         this.initTarget(id, sGroup, config);
20177         if (!Roo.isTouch) {
20178             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20179         }
20180         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20181         // Event.on(this.id, "selectstart", Event.preventDefault);
20182     },
20183
20184     /**
20185      * Initializes Targeting functionality only... the object does not
20186      * get a mousedown handler.
20187      * @method initTarget
20188      * @param id the id of the linked element
20189      * @param {String} sGroup the group of related items
20190      * @param {object} config configuration attributes
20191      */
20192     initTarget: function(id, sGroup, config) {
20193
20194         // configuration attributes
20195         this.config = config || {};
20196
20197         // create a local reference to the drag and drop manager
20198         this.DDM = Roo.dd.DDM;
20199         // initialize the groups array
20200         this.groups = {};
20201
20202         // assume that we have an element reference instead of an id if the
20203         // parameter is not a string
20204         if (typeof id !== "string") {
20205             id = Roo.id(id);
20206         }
20207
20208         // set the id
20209         this.id = id;
20210
20211         // add to an interaction group
20212         this.addToGroup((sGroup) ? sGroup : "default");
20213
20214         // We don't want to register this as the handle with the manager
20215         // so we just set the id rather than calling the setter.
20216         this.handleElId = id;
20217
20218         // the linked element is the element that gets dragged by default
20219         this.setDragElId(id);
20220
20221         // by default, clicked anchors will not start drag operations.
20222         this.invalidHandleTypes = { A: "A" };
20223         this.invalidHandleIds = {};
20224         this.invalidHandleClasses = [];
20225
20226         this.applyConfig();
20227
20228         this.handleOnAvailable();
20229     },
20230
20231     /**
20232      * Applies the configuration parameters that were passed into the constructor.
20233      * This is supposed to happen at each level through the inheritance chain.  So
20234      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20235      * DragDrop in order to get all of the parameters that are available in
20236      * each object.
20237      * @method applyConfig
20238      */
20239     applyConfig: function() {
20240
20241         // configurable properties:
20242         //    padding, isTarget, maintainOffset, primaryButtonOnly
20243         this.padding           = this.config.padding || [0, 0, 0, 0];
20244         this.isTarget          = (this.config.isTarget !== false);
20245         this.maintainOffset    = (this.config.maintainOffset);
20246         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20247
20248     },
20249
20250     /**
20251      * Executed when the linked element is available
20252      * @method handleOnAvailable
20253      * @private
20254      */
20255     handleOnAvailable: function() {
20256         this.available = true;
20257         this.resetConstraints();
20258         this.onAvailable();
20259     },
20260
20261      /**
20262      * Configures the padding for the target zone in px.  Effectively expands
20263      * (or reduces) the virtual object size for targeting calculations.
20264      * Supports css-style shorthand; if only one parameter is passed, all sides
20265      * will have that padding, and if only two are passed, the top and bottom
20266      * will have the first param, the left and right the second.
20267      * @method setPadding
20268      * @param {int} iTop    Top pad
20269      * @param {int} iRight  Right pad
20270      * @param {int} iBot    Bot pad
20271      * @param {int} iLeft   Left pad
20272      */
20273     setPadding: function(iTop, iRight, iBot, iLeft) {
20274         // this.padding = [iLeft, iRight, iTop, iBot];
20275         if (!iRight && 0 !== iRight) {
20276             this.padding = [iTop, iTop, iTop, iTop];
20277         } else if (!iBot && 0 !== iBot) {
20278             this.padding = [iTop, iRight, iTop, iRight];
20279         } else {
20280             this.padding = [iTop, iRight, iBot, iLeft];
20281         }
20282     },
20283
20284     /**
20285      * Stores the initial placement of the linked element.
20286      * @method setInitialPosition
20287      * @param {int} diffX   the X offset, default 0
20288      * @param {int} diffY   the Y offset, default 0
20289      */
20290     setInitPosition: function(diffX, diffY) {
20291         var el = this.getEl();
20292
20293         if (!this.DDM.verifyEl(el)) {
20294             return;
20295         }
20296
20297         var dx = diffX || 0;
20298         var dy = diffY || 0;
20299
20300         var p = Dom.getXY( el );
20301
20302         this.initPageX = p[0] - dx;
20303         this.initPageY = p[1] - dy;
20304
20305         this.lastPageX = p[0];
20306         this.lastPageY = p[1];
20307
20308
20309         this.setStartPosition(p);
20310     },
20311
20312     /**
20313      * Sets the start position of the element.  This is set when the obj
20314      * is initialized, the reset when a drag is started.
20315      * @method setStartPosition
20316      * @param pos current position (from previous lookup)
20317      * @private
20318      */
20319     setStartPosition: function(pos) {
20320         var p = pos || Dom.getXY( this.getEl() );
20321         this.deltaSetXY = null;
20322
20323         this.startPageX = p[0];
20324         this.startPageY = p[1];
20325     },
20326
20327     /**
20328      * Add this instance to a group of related drag/drop objects.  All
20329      * instances belong to at least one group, and can belong to as many
20330      * groups as needed.
20331      * @method addToGroup
20332      * @param sGroup {string} the name of the group
20333      */
20334     addToGroup: function(sGroup) {
20335         this.groups[sGroup] = true;
20336         this.DDM.regDragDrop(this, sGroup);
20337     },
20338
20339     /**
20340      * Remove's this instance from the supplied interaction group
20341      * @method removeFromGroup
20342      * @param {string}  sGroup  The group to drop
20343      */
20344     removeFromGroup: function(sGroup) {
20345         if (this.groups[sGroup]) {
20346             delete this.groups[sGroup];
20347         }
20348
20349         this.DDM.removeDDFromGroup(this, sGroup);
20350     },
20351
20352     /**
20353      * Allows you to specify that an element other than the linked element
20354      * will be moved with the cursor during a drag
20355      * @method setDragElId
20356      * @param id {string} the id of the element that will be used to initiate the drag
20357      */
20358     setDragElId: function(id) {
20359         this.dragElId = id;
20360     },
20361
20362     /**
20363      * Allows you to specify a child of the linked element that should be
20364      * used to initiate the drag operation.  An example of this would be if
20365      * you have a content div with text and links.  Clicking anywhere in the
20366      * content area would normally start the drag operation.  Use this method
20367      * to specify that an element inside of the content div is the element
20368      * that starts the drag operation.
20369      * @method setHandleElId
20370      * @param id {string} the id of the element that will be used to
20371      * initiate the drag.
20372      */
20373     setHandleElId: function(id) {
20374         if (typeof id !== "string") {
20375             id = Roo.id(id);
20376         }
20377         this.handleElId = id;
20378         this.DDM.regHandle(this.id, id);
20379     },
20380
20381     /**
20382      * Allows you to set an element outside of the linked element as a drag
20383      * handle
20384      * @method setOuterHandleElId
20385      * @param id the id of the element that will be used to initiate the drag
20386      */
20387     setOuterHandleElId: function(id) {
20388         if (typeof id !== "string") {
20389             id = Roo.id(id);
20390         }
20391         Event.on(id, "mousedown",
20392                 this.handleMouseDown, this);
20393         this.setHandleElId(id);
20394
20395         this.hasOuterHandles = true;
20396     },
20397
20398     /**
20399      * Remove all drag and drop hooks for this element
20400      * @method unreg
20401      */
20402     unreg: function() {
20403         Event.un(this.id, "mousedown",
20404                 this.handleMouseDown);
20405         Event.un(this.id, "touchstart",
20406                 this.handleMouseDown);
20407         this._domRef = null;
20408         this.DDM._remove(this);
20409     },
20410
20411     destroy : function(){
20412         this.unreg();
20413     },
20414
20415     /**
20416      * Returns true if this instance is locked, or the drag drop mgr is locked
20417      * (meaning that all drag/drop is disabled on the page.)
20418      * @method isLocked
20419      * @return {boolean} true if this obj or all drag/drop is locked, else
20420      * false
20421      */
20422     isLocked: function() {
20423         return (this.DDM.isLocked() || this.locked);
20424     },
20425
20426     /**
20427      * Fired when this object is clicked
20428      * @method handleMouseDown
20429      * @param {Event} e
20430      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20431      * @private
20432      */
20433     handleMouseDown: function(e, oDD){
20434      
20435         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20436             //Roo.log('not touch/ button !=0');
20437             return;
20438         }
20439         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20440             return; // double touch..
20441         }
20442         
20443
20444         if (this.isLocked()) {
20445             //Roo.log('locked');
20446             return;
20447         }
20448
20449         this.DDM.refreshCache(this.groups);
20450 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20451         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20452         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20453             //Roo.log('no outer handes or not over target');
20454                 // do nothing.
20455         } else {
20456 //            Roo.log('check validator');
20457             if (this.clickValidator(e)) {
20458 //                Roo.log('validate success');
20459                 // set the initial element position
20460                 this.setStartPosition();
20461
20462
20463                 this.b4MouseDown(e);
20464                 this.onMouseDown(e);
20465
20466                 this.DDM.handleMouseDown(e, this);
20467
20468                 this.DDM.stopEvent(e);
20469             } else {
20470
20471
20472             }
20473         }
20474     },
20475
20476     clickValidator: function(e) {
20477         var target = e.getTarget();
20478         return ( this.isValidHandleChild(target) &&
20479                     (this.id == this.handleElId ||
20480                         this.DDM.handleWasClicked(target, this.id)) );
20481     },
20482
20483     /**
20484      * Allows you to specify a tag name that should not start a drag operation
20485      * when clicked.  This is designed to facilitate embedding links within a
20486      * drag handle that do something other than start the drag.
20487      * @method addInvalidHandleType
20488      * @param {string} tagName the type of element to exclude
20489      */
20490     addInvalidHandleType: function(tagName) {
20491         var type = tagName.toUpperCase();
20492         this.invalidHandleTypes[type] = type;
20493     },
20494
20495     /**
20496      * Lets you to specify an element id for a child of a drag handle
20497      * that should not initiate a drag
20498      * @method addInvalidHandleId
20499      * @param {string} id the element id of the element you wish to ignore
20500      */
20501     addInvalidHandleId: function(id) {
20502         if (typeof id !== "string") {
20503             id = Roo.id(id);
20504         }
20505         this.invalidHandleIds[id] = id;
20506     },
20507
20508     /**
20509      * Lets you specify a css class of elements that will not initiate a drag
20510      * @method addInvalidHandleClass
20511      * @param {string} cssClass the class of the elements you wish to ignore
20512      */
20513     addInvalidHandleClass: function(cssClass) {
20514         this.invalidHandleClasses.push(cssClass);
20515     },
20516
20517     /**
20518      * Unsets an excluded tag name set by addInvalidHandleType
20519      * @method removeInvalidHandleType
20520      * @param {string} tagName the type of element to unexclude
20521      */
20522     removeInvalidHandleType: function(tagName) {
20523         var type = tagName.toUpperCase();
20524         // this.invalidHandleTypes[type] = null;
20525         delete this.invalidHandleTypes[type];
20526     },
20527
20528     /**
20529      * Unsets an invalid handle id
20530      * @method removeInvalidHandleId
20531      * @param {string} id the id of the element to re-enable
20532      */
20533     removeInvalidHandleId: function(id) {
20534         if (typeof id !== "string") {
20535             id = Roo.id(id);
20536         }
20537         delete this.invalidHandleIds[id];
20538     },
20539
20540     /**
20541      * Unsets an invalid css class
20542      * @method removeInvalidHandleClass
20543      * @param {string} cssClass the class of the element(s) you wish to
20544      * re-enable
20545      */
20546     removeInvalidHandleClass: function(cssClass) {
20547         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20548             if (this.invalidHandleClasses[i] == cssClass) {
20549                 delete this.invalidHandleClasses[i];
20550             }
20551         }
20552     },
20553
20554     /**
20555      * Checks the tag exclusion list to see if this click should be ignored
20556      * @method isValidHandleChild
20557      * @param {HTMLElement} node the HTMLElement to evaluate
20558      * @return {boolean} true if this is a valid tag type, false if not
20559      */
20560     isValidHandleChild: function(node) {
20561
20562         var valid = true;
20563         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20564         var nodeName;
20565         try {
20566             nodeName = node.nodeName.toUpperCase();
20567         } catch(e) {
20568             nodeName = node.nodeName;
20569         }
20570         valid = valid && !this.invalidHandleTypes[nodeName];
20571         valid = valid && !this.invalidHandleIds[node.id];
20572
20573         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20574             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20575         }
20576
20577
20578         return valid;
20579
20580     },
20581
20582     /**
20583      * Create the array of horizontal tick marks if an interval was specified
20584      * in setXConstraint().
20585      * @method setXTicks
20586      * @private
20587      */
20588     setXTicks: function(iStartX, iTickSize) {
20589         this.xTicks = [];
20590         this.xTickSize = iTickSize;
20591
20592         var tickMap = {};
20593
20594         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20595             if (!tickMap[i]) {
20596                 this.xTicks[this.xTicks.length] = i;
20597                 tickMap[i] = true;
20598             }
20599         }
20600
20601         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20602             if (!tickMap[i]) {
20603                 this.xTicks[this.xTicks.length] = i;
20604                 tickMap[i] = true;
20605             }
20606         }
20607
20608         this.xTicks.sort(this.DDM.numericSort) ;
20609     },
20610
20611     /**
20612      * Create the array of vertical tick marks if an interval was specified in
20613      * setYConstraint().
20614      * @method setYTicks
20615      * @private
20616      */
20617     setYTicks: function(iStartY, iTickSize) {
20618         this.yTicks = [];
20619         this.yTickSize = iTickSize;
20620
20621         var tickMap = {};
20622
20623         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20624             if (!tickMap[i]) {
20625                 this.yTicks[this.yTicks.length] = i;
20626                 tickMap[i] = true;
20627             }
20628         }
20629
20630         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20631             if (!tickMap[i]) {
20632                 this.yTicks[this.yTicks.length] = i;
20633                 tickMap[i] = true;
20634             }
20635         }
20636
20637         this.yTicks.sort(this.DDM.numericSort) ;
20638     },
20639
20640     /**
20641      * By default, the element can be dragged any place on the screen.  Use
20642      * this method to limit the horizontal travel of the element.  Pass in
20643      * 0,0 for the parameters if you want to lock the drag to the y axis.
20644      * @method setXConstraint
20645      * @param {int} iLeft the number of pixels the element can move to the left
20646      * @param {int} iRight the number of pixels the element can move to the
20647      * right
20648      * @param {int} iTickSize optional parameter for specifying that the
20649      * element
20650      * should move iTickSize pixels at a time.
20651      */
20652     setXConstraint: function(iLeft, iRight, iTickSize) {
20653         this.leftConstraint = iLeft;
20654         this.rightConstraint = iRight;
20655
20656         this.minX = this.initPageX - iLeft;
20657         this.maxX = this.initPageX + iRight;
20658         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20659
20660         this.constrainX = true;
20661     },
20662
20663     /**
20664      * Clears any constraints applied to this instance.  Also clears ticks
20665      * since they can't exist independent of a constraint at this time.
20666      * @method clearConstraints
20667      */
20668     clearConstraints: function() {
20669         this.constrainX = false;
20670         this.constrainY = false;
20671         this.clearTicks();
20672     },
20673
20674     /**
20675      * Clears any tick interval defined for this instance
20676      * @method clearTicks
20677      */
20678     clearTicks: function() {
20679         this.xTicks = null;
20680         this.yTicks = null;
20681         this.xTickSize = 0;
20682         this.yTickSize = 0;
20683     },
20684
20685     /**
20686      * By default, the element can be dragged any place on the screen.  Set
20687      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20688      * parameters if you want to lock the drag to the x axis.
20689      * @method setYConstraint
20690      * @param {int} iUp the number of pixels the element can move up
20691      * @param {int} iDown the number of pixels the element can move down
20692      * @param {int} iTickSize optional parameter for specifying that the
20693      * element should move iTickSize pixels at a time.
20694      */
20695     setYConstraint: function(iUp, iDown, iTickSize) {
20696         this.topConstraint = iUp;
20697         this.bottomConstraint = iDown;
20698
20699         this.minY = this.initPageY - iUp;
20700         this.maxY = this.initPageY + iDown;
20701         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20702
20703         this.constrainY = true;
20704
20705     },
20706
20707     /**
20708      * resetConstraints must be called if you manually reposition a dd element.
20709      * @method resetConstraints
20710      * @param {boolean} maintainOffset
20711      */
20712     resetConstraints: function() {
20713
20714
20715         // Maintain offsets if necessary
20716         if (this.initPageX || this.initPageX === 0) {
20717             // figure out how much this thing has moved
20718             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20719             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20720
20721             this.setInitPosition(dx, dy);
20722
20723         // This is the first time we have detected the element's position
20724         } else {
20725             this.setInitPosition();
20726         }
20727
20728         if (this.constrainX) {
20729             this.setXConstraint( this.leftConstraint,
20730                                  this.rightConstraint,
20731                                  this.xTickSize        );
20732         }
20733
20734         if (this.constrainY) {
20735             this.setYConstraint( this.topConstraint,
20736                                  this.bottomConstraint,
20737                                  this.yTickSize         );
20738         }
20739     },
20740
20741     /**
20742      * Normally the drag element is moved pixel by pixel, but we can specify
20743      * that it move a number of pixels at a time.  This method resolves the
20744      * location when we have it set up like this.
20745      * @method getTick
20746      * @param {int} val where we want to place the object
20747      * @param {int[]} tickArray sorted array of valid points
20748      * @return {int} the closest tick
20749      * @private
20750      */
20751     getTick: function(val, tickArray) {
20752
20753         if (!tickArray) {
20754             // If tick interval is not defined, it is effectively 1 pixel,
20755             // so we return the value passed to us.
20756             return val;
20757         } else if (tickArray[0] >= val) {
20758             // The value is lower than the first tick, so we return the first
20759             // tick.
20760             return tickArray[0];
20761         } else {
20762             for (var i=0, len=tickArray.length; i<len; ++i) {
20763                 var next = i + 1;
20764                 if (tickArray[next] && tickArray[next] >= val) {
20765                     var diff1 = val - tickArray[i];
20766                     var diff2 = tickArray[next] - val;
20767                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20768                 }
20769             }
20770
20771             // The value is larger than the last tick, so we return the last
20772             // tick.
20773             return tickArray[tickArray.length - 1];
20774         }
20775     },
20776
20777     /**
20778      * toString method
20779      * @method toString
20780      * @return {string} string representation of the dd obj
20781      */
20782     toString: function() {
20783         return ("DragDrop " + this.id);
20784     }
20785
20786 });
20787
20788 })();
20789 /*
20790  * Based on:
20791  * Ext JS Library 1.1.1
20792  * Copyright(c) 2006-2007, Ext JS, LLC.
20793  *
20794  * Originally Released Under LGPL - original licence link has changed is not relivant.
20795  *
20796  * Fork - LGPL
20797  * <script type="text/javascript">
20798  */
20799
20800
20801 /**
20802  * The drag and drop utility provides a framework for building drag and drop
20803  * applications.  In addition to enabling drag and drop for specific elements,
20804  * the drag and drop elements are tracked by the manager class, and the
20805  * interactions between the various elements are tracked during the drag and
20806  * the implementing code is notified about these important moments.
20807  */
20808
20809 // Only load the library once.  Rewriting the manager class would orphan
20810 // existing drag and drop instances.
20811 if (!Roo.dd.DragDropMgr) {
20812
20813 /**
20814  * @class Roo.dd.DragDropMgr
20815  * DragDropMgr is a singleton that tracks the element interaction for
20816  * all DragDrop items in the window.  Generally, you will not call
20817  * this class directly, but it does have helper methods that could
20818  * be useful in your DragDrop implementations.
20819  * @static
20820  */
20821 Roo.dd.DragDropMgr = function() {
20822
20823     var Event = Roo.EventManager;
20824
20825     return {
20826
20827         /**
20828          * Two dimensional Array of registered DragDrop objects.  The first
20829          * dimension is the DragDrop item group, the second the DragDrop
20830          * object.
20831          * @property ids
20832          * @type {string: string}
20833          * @private
20834          * @static
20835          */
20836         ids: {},
20837
20838         /**
20839          * Array of element ids defined as drag handles.  Used to determine
20840          * if the element that generated the mousedown event is actually the
20841          * handle and not the html element itself.
20842          * @property handleIds
20843          * @type {string: string}
20844          * @private
20845          * @static
20846          */
20847         handleIds: {},
20848
20849         /**
20850          * the DragDrop object that is currently being dragged
20851          * @property dragCurrent
20852          * @type DragDrop
20853          * @private
20854          * @static
20855          **/
20856         dragCurrent: null,
20857
20858         /**
20859          * the DragDrop object(s) that are being hovered over
20860          * @property dragOvers
20861          * @type Array
20862          * @private
20863          * @static
20864          */
20865         dragOvers: {},
20866
20867         /**
20868          * the X distance between the cursor and the object being dragged
20869          * @property deltaX
20870          * @type int
20871          * @private
20872          * @static
20873          */
20874         deltaX: 0,
20875
20876         /**
20877          * the Y distance between the cursor and the object being dragged
20878          * @property deltaY
20879          * @type int
20880          * @private
20881          * @static
20882          */
20883         deltaY: 0,
20884
20885         /**
20886          * Flag to determine if we should prevent the default behavior of the
20887          * events we define. By default this is true, but this can be set to
20888          * false if you need the default behavior (not recommended)
20889          * @property preventDefault
20890          * @type boolean
20891          * @static
20892          */
20893         preventDefault: true,
20894
20895         /**
20896          * Flag to determine if we should stop the propagation of the events
20897          * we generate. This is true by default but you may want to set it to
20898          * false if the html element contains other features that require the
20899          * mouse click.
20900          * @property stopPropagation
20901          * @type boolean
20902          * @static
20903          */
20904         stopPropagation: true,
20905
20906         /**
20907          * Internal flag that is set to true when drag and drop has been
20908          * intialized
20909          * @property initialized
20910          * @private
20911          * @static
20912          */
20913         initalized: false,
20914
20915         /**
20916          * All drag and drop can be disabled.
20917          * @property locked
20918          * @private
20919          * @static
20920          */
20921         locked: false,
20922
20923         /**
20924          * Called the first time an element is registered.
20925          * @method init
20926          * @private
20927          * @static
20928          */
20929         init: function() {
20930             this.initialized = true;
20931         },
20932
20933         /**
20934          * In point mode, drag and drop interaction is defined by the
20935          * location of the cursor during the drag/drop
20936          * @property POINT
20937          * @type int
20938          * @static
20939          */
20940         POINT: 0,
20941
20942         /**
20943          * In intersect mode, drag and drop interactio nis defined by the
20944          * overlap of two or more drag and drop objects.
20945          * @property INTERSECT
20946          * @type int
20947          * @static
20948          */
20949         INTERSECT: 1,
20950
20951         /**
20952          * The current drag and drop mode.  Default: POINT
20953          * @property mode
20954          * @type int
20955          * @static
20956          */
20957         mode: 0,
20958
20959         /**
20960          * Runs method on all drag and drop objects
20961          * @method _execOnAll
20962          * @private
20963          * @static
20964          */
20965         _execOnAll: function(sMethod, args) {
20966             for (var i in this.ids) {
20967                 for (var j in this.ids[i]) {
20968                     var oDD = this.ids[i][j];
20969                     if (! this.isTypeOfDD(oDD)) {
20970                         continue;
20971                     }
20972                     oDD[sMethod].apply(oDD, args);
20973                 }
20974             }
20975         },
20976
20977         /**
20978          * Drag and drop initialization.  Sets up the global event handlers
20979          * @method _onLoad
20980          * @private
20981          * @static
20982          */
20983         _onLoad: function() {
20984
20985             this.init();
20986
20987             if (!Roo.isTouch) {
20988                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20989                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20990             }
20991             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20992             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20993             
20994             Event.on(window,   "unload",    this._onUnload, this, true);
20995             Event.on(window,   "resize",    this._onResize, this, true);
20996             // Event.on(window,   "mouseout",    this._test);
20997
20998         },
20999
21000         /**
21001          * Reset constraints on all drag and drop objs
21002          * @method _onResize
21003          * @private
21004          * @static
21005          */
21006         _onResize: function(e) {
21007             this._execOnAll("resetConstraints", []);
21008         },
21009
21010         /**
21011          * Lock all drag and drop functionality
21012          * @method lock
21013          * @static
21014          */
21015         lock: function() { this.locked = true; },
21016
21017         /**
21018          * Unlock all drag and drop functionality
21019          * @method unlock
21020          * @static
21021          */
21022         unlock: function() { this.locked = false; },
21023
21024         /**
21025          * Is drag and drop locked?
21026          * @method isLocked
21027          * @return {boolean} True if drag and drop is locked, false otherwise.
21028          * @static
21029          */
21030         isLocked: function() { return this.locked; },
21031
21032         /**
21033          * Location cache that is set for all drag drop objects when a drag is
21034          * initiated, cleared when the drag is finished.
21035          * @property locationCache
21036          * @private
21037          * @static
21038          */
21039         locationCache: {},
21040
21041         /**
21042          * Set useCache to false if you want to force object the lookup of each
21043          * drag and drop linked element constantly during a drag.
21044          * @property useCache
21045          * @type boolean
21046          * @static
21047          */
21048         useCache: true,
21049
21050         /**
21051          * The number of pixels that the mouse needs to move after the
21052          * mousedown before the drag is initiated.  Default=3;
21053          * @property clickPixelThresh
21054          * @type int
21055          * @static
21056          */
21057         clickPixelThresh: 3,
21058
21059         /**
21060          * The number of milliseconds after the mousedown event to initiate the
21061          * drag if we don't get a mouseup event. Default=1000
21062          * @property clickTimeThresh
21063          * @type int
21064          * @static
21065          */
21066         clickTimeThresh: 350,
21067
21068         /**
21069          * Flag that indicates that either the drag pixel threshold or the
21070          * mousdown time threshold has been met
21071          * @property dragThreshMet
21072          * @type boolean
21073          * @private
21074          * @static
21075          */
21076         dragThreshMet: false,
21077
21078         /**
21079          * Timeout used for the click time threshold
21080          * @property clickTimeout
21081          * @type Object
21082          * @private
21083          * @static
21084          */
21085         clickTimeout: null,
21086
21087         /**
21088          * The X position of the mousedown event stored for later use when a
21089          * drag threshold is met.
21090          * @property startX
21091          * @type int
21092          * @private
21093          * @static
21094          */
21095         startX: 0,
21096
21097         /**
21098          * The Y position of the mousedown event stored for later use when a
21099          * drag threshold is met.
21100          * @property startY
21101          * @type int
21102          * @private
21103          * @static
21104          */
21105         startY: 0,
21106
21107         /**
21108          * Each DragDrop instance must be registered with the DragDropMgr.
21109          * This is executed in DragDrop.init()
21110          * @method regDragDrop
21111          * @param {DragDrop} oDD the DragDrop object to register
21112          * @param {String} sGroup the name of the group this element belongs to
21113          * @static
21114          */
21115         regDragDrop: function(oDD, sGroup) {
21116             if (!this.initialized) { this.init(); }
21117
21118             if (!this.ids[sGroup]) {
21119                 this.ids[sGroup] = {};
21120             }
21121             this.ids[sGroup][oDD.id] = oDD;
21122         },
21123
21124         /**
21125          * Removes the supplied dd instance from the supplied group. Executed
21126          * by DragDrop.removeFromGroup, so don't call this function directly.
21127          * @method removeDDFromGroup
21128          * @private
21129          * @static
21130          */
21131         removeDDFromGroup: function(oDD, sGroup) {
21132             if (!this.ids[sGroup]) {
21133                 this.ids[sGroup] = {};
21134             }
21135
21136             var obj = this.ids[sGroup];
21137             if (obj && obj[oDD.id]) {
21138                 delete obj[oDD.id];
21139             }
21140         },
21141
21142         /**
21143          * Unregisters a drag and drop item.  This is executed in
21144          * DragDrop.unreg, use that method instead of calling this directly.
21145          * @method _remove
21146          * @private
21147          * @static
21148          */
21149         _remove: function(oDD) {
21150             for (var g in oDD.groups) {
21151                 if (g && this.ids[g][oDD.id]) {
21152                     delete this.ids[g][oDD.id];
21153                 }
21154             }
21155             delete this.handleIds[oDD.id];
21156         },
21157
21158         /**
21159          * Each DragDrop handle element must be registered.  This is done
21160          * automatically when executing DragDrop.setHandleElId()
21161          * @method regHandle
21162          * @param {String} sDDId the DragDrop id this element is a handle for
21163          * @param {String} sHandleId the id of the element that is the drag
21164          * handle
21165          * @static
21166          */
21167         regHandle: function(sDDId, sHandleId) {
21168             if (!this.handleIds[sDDId]) {
21169                 this.handleIds[sDDId] = {};
21170             }
21171             this.handleIds[sDDId][sHandleId] = sHandleId;
21172         },
21173
21174         /**
21175          * Utility function to determine if a given element has been
21176          * registered as a drag drop item.
21177          * @method isDragDrop
21178          * @param {String} id the element id to check
21179          * @return {boolean} true if this element is a DragDrop item,
21180          * false otherwise
21181          * @static
21182          */
21183         isDragDrop: function(id) {
21184             return ( this.getDDById(id) ) ? true : false;
21185         },
21186
21187         /**
21188          * Returns the drag and drop instances that are in all groups the
21189          * passed in instance belongs to.
21190          * @method getRelated
21191          * @param {DragDrop} p_oDD the obj to get related data for
21192          * @param {boolean} bTargetsOnly if true, only return targetable objs
21193          * @return {DragDrop[]} the related instances
21194          * @static
21195          */
21196         getRelated: function(p_oDD, bTargetsOnly) {
21197             var oDDs = [];
21198             for (var i in p_oDD.groups) {
21199                 for (j in this.ids[i]) {
21200                     var dd = this.ids[i][j];
21201                     if (! this.isTypeOfDD(dd)) {
21202                         continue;
21203                     }
21204                     if (!bTargetsOnly || dd.isTarget) {
21205                         oDDs[oDDs.length] = dd;
21206                     }
21207                 }
21208             }
21209
21210             return oDDs;
21211         },
21212
21213         /**
21214          * Returns true if the specified dd target is a legal target for
21215          * the specifice drag obj
21216          * @method isLegalTarget
21217          * @param {DragDrop} the drag obj
21218          * @param {DragDrop} the target
21219          * @return {boolean} true if the target is a legal target for the
21220          * dd obj
21221          * @static
21222          */
21223         isLegalTarget: function (oDD, oTargetDD) {
21224             var targets = this.getRelated(oDD, true);
21225             for (var i=0, len=targets.length;i<len;++i) {
21226                 if (targets[i].id == oTargetDD.id) {
21227                     return true;
21228                 }
21229             }
21230
21231             return false;
21232         },
21233
21234         /**
21235          * My goal is to be able to transparently determine if an object is
21236          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21237          * returns "object", oDD.constructor.toString() always returns
21238          * "DragDrop" and not the name of the subclass.  So for now it just
21239          * evaluates a well-known variable in DragDrop.
21240          * @method isTypeOfDD
21241          * @param {Object} the object to evaluate
21242          * @return {boolean} true if typeof oDD = DragDrop
21243          * @static
21244          */
21245         isTypeOfDD: function (oDD) {
21246             return (oDD && oDD.__ygDragDrop);
21247         },
21248
21249         /**
21250          * Utility function to determine if a given element has been
21251          * registered as a drag drop handle for the given Drag Drop object.
21252          * @method isHandle
21253          * @param {String} id the element id to check
21254          * @return {boolean} true if this element is a DragDrop handle, false
21255          * otherwise
21256          * @static
21257          */
21258         isHandle: function(sDDId, sHandleId) {
21259             return ( this.handleIds[sDDId] &&
21260                             this.handleIds[sDDId][sHandleId] );
21261         },
21262
21263         /**
21264          * Returns the DragDrop instance for a given id
21265          * @method getDDById
21266          * @param {String} id the id of the DragDrop object
21267          * @return {DragDrop} the drag drop object, null if it is not found
21268          * @static
21269          */
21270         getDDById: function(id) {
21271             for (var i in this.ids) {
21272                 if (this.ids[i][id]) {
21273                     return this.ids[i][id];
21274                 }
21275             }
21276             return null;
21277         },
21278
21279         /**
21280          * Fired after a registered DragDrop object gets the mousedown event.
21281          * Sets up the events required to track the object being dragged
21282          * @method handleMouseDown
21283          * @param {Event} e the event
21284          * @param oDD the DragDrop object being dragged
21285          * @private
21286          * @static
21287          */
21288         handleMouseDown: function(e, oDD) {
21289             if(Roo.QuickTips){
21290                 Roo.QuickTips.disable();
21291             }
21292             this.currentTarget = e.getTarget();
21293
21294             this.dragCurrent = oDD;
21295
21296             var el = oDD.getEl();
21297
21298             // track start position
21299             this.startX = e.getPageX();
21300             this.startY = e.getPageY();
21301
21302             this.deltaX = this.startX - el.offsetLeft;
21303             this.deltaY = this.startY - el.offsetTop;
21304
21305             this.dragThreshMet = false;
21306
21307             this.clickTimeout = setTimeout(
21308                     function() {
21309                         var DDM = Roo.dd.DDM;
21310                         DDM.startDrag(DDM.startX, DDM.startY);
21311                     },
21312                     this.clickTimeThresh );
21313         },
21314
21315         /**
21316          * Fired when either the drag pixel threshol or the mousedown hold
21317          * time threshold has been met.
21318          * @method startDrag
21319          * @param x {int} the X position of the original mousedown
21320          * @param y {int} the Y position of the original mousedown
21321          * @static
21322          */
21323         startDrag: function(x, y) {
21324             clearTimeout(this.clickTimeout);
21325             if (this.dragCurrent) {
21326                 this.dragCurrent.b4StartDrag(x, y);
21327                 this.dragCurrent.startDrag(x, y);
21328             }
21329             this.dragThreshMet = true;
21330         },
21331
21332         /**
21333          * Internal function to handle the mouseup event.  Will be invoked
21334          * from the context of the document.
21335          * @method handleMouseUp
21336          * @param {Event} e the event
21337          * @private
21338          * @static
21339          */
21340         handleMouseUp: function(e) {
21341
21342             if(Roo.QuickTips){
21343                 Roo.QuickTips.enable();
21344             }
21345             if (! this.dragCurrent) {
21346                 return;
21347             }
21348
21349             clearTimeout(this.clickTimeout);
21350
21351             if (this.dragThreshMet) {
21352                 this.fireEvents(e, true);
21353             } else {
21354             }
21355
21356             this.stopDrag(e);
21357
21358             this.stopEvent(e);
21359         },
21360
21361         /**
21362          * Utility to stop event propagation and event default, if these
21363          * features are turned on.
21364          * @method stopEvent
21365          * @param {Event} e the event as returned by this.getEvent()
21366          * @static
21367          */
21368         stopEvent: function(e){
21369             if(this.stopPropagation) {
21370                 e.stopPropagation();
21371             }
21372
21373             if (this.preventDefault) {
21374                 e.preventDefault();
21375             }
21376         },
21377
21378         /**
21379          * Internal function to clean up event handlers after the drag
21380          * operation is complete
21381          * @method stopDrag
21382          * @param {Event} e the event
21383          * @private
21384          * @static
21385          */
21386         stopDrag: function(e) {
21387             // Fire the drag end event for the item that was dragged
21388             if (this.dragCurrent) {
21389                 if (this.dragThreshMet) {
21390                     this.dragCurrent.b4EndDrag(e);
21391                     this.dragCurrent.endDrag(e);
21392                 }
21393
21394                 this.dragCurrent.onMouseUp(e);
21395             }
21396
21397             this.dragCurrent = null;
21398             this.dragOvers = {};
21399         },
21400
21401         /**
21402          * Internal function to handle the mousemove event.  Will be invoked
21403          * from the context of the html element.
21404          *
21405          * @TODO figure out what we can do about mouse events lost when the
21406          * user drags objects beyond the window boundary.  Currently we can
21407          * detect this in internet explorer by verifying that the mouse is
21408          * down during the mousemove event.  Firefox doesn't give us the
21409          * button state on the mousemove event.
21410          * @method handleMouseMove
21411          * @param {Event} e the event
21412          * @private
21413          * @static
21414          */
21415         handleMouseMove: function(e) {
21416             if (! this.dragCurrent) {
21417                 return true;
21418             }
21419
21420             // var button = e.which || e.button;
21421
21422             // check for IE mouseup outside of page boundary
21423             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21424                 this.stopEvent(e);
21425                 return this.handleMouseUp(e);
21426             }
21427
21428             if (!this.dragThreshMet) {
21429                 var diffX = Math.abs(this.startX - e.getPageX());
21430                 var diffY = Math.abs(this.startY - e.getPageY());
21431                 if (diffX > this.clickPixelThresh ||
21432                             diffY > this.clickPixelThresh) {
21433                     this.startDrag(this.startX, this.startY);
21434                 }
21435             }
21436
21437             if (this.dragThreshMet) {
21438                 this.dragCurrent.b4Drag(e);
21439                 this.dragCurrent.onDrag(e);
21440                 if(!this.dragCurrent.moveOnly){
21441                     this.fireEvents(e, false);
21442                 }
21443             }
21444
21445             this.stopEvent(e);
21446
21447             return true;
21448         },
21449
21450         /**
21451          * Iterates over all of the DragDrop elements to find ones we are
21452          * hovering over or dropping on
21453          * @method fireEvents
21454          * @param {Event} e the event
21455          * @param {boolean} isDrop is this a drop op or a mouseover op?
21456          * @private
21457          * @static
21458          */
21459         fireEvents: function(e, isDrop) {
21460             var dc = this.dragCurrent;
21461
21462             // If the user did the mouse up outside of the window, we could
21463             // get here even though we have ended the drag.
21464             if (!dc || dc.isLocked()) {
21465                 return;
21466             }
21467
21468             var pt = e.getPoint();
21469
21470             // cache the previous dragOver array
21471             var oldOvers = [];
21472
21473             var outEvts   = [];
21474             var overEvts  = [];
21475             var dropEvts  = [];
21476             var enterEvts = [];
21477
21478             // Check to see if the object(s) we were hovering over is no longer
21479             // being hovered over so we can fire the onDragOut event
21480             for (var i in this.dragOvers) {
21481
21482                 var ddo = this.dragOvers[i];
21483
21484                 if (! this.isTypeOfDD(ddo)) {
21485                     continue;
21486                 }
21487
21488                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21489                     outEvts.push( ddo );
21490                 }
21491
21492                 oldOvers[i] = true;
21493                 delete this.dragOvers[i];
21494             }
21495
21496             for (var sGroup in dc.groups) {
21497
21498                 if ("string" != typeof sGroup) {
21499                     continue;
21500                 }
21501
21502                 for (i in this.ids[sGroup]) {
21503                     var oDD = this.ids[sGroup][i];
21504                     if (! this.isTypeOfDD(oDD)) {
21505                         continue;
21506                     }
21507
21508                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21509                         if (this.isOverTarget(pt, oDD, this.mode)) {
21510                             // look for drop interactions
21511                             if (isDrop) {
21512                                 dropEvts.push( oDD );
21513                             // look for drag enter and drag over interactions
21514                             } else {
21515
21516                                 // initial drag over: dragEnter fires
21517                                 if (!oldOvers[oDD.id]) {
21518                                     enterEvts.push( oDD );
21519                                 // subsequent drag overs: dragOver fires
21520                                 } else {
21521                                     overEvts.push( oDD );
21522                                 }
21523
21524                                 this.dragOvers[oDD.id] = oDD;
21525                             }
21526                         }
21527                     }
21528                 }
21529             }
21530
21531             if (this.mode) {
21532                 if (outEvts.length) {
21533                     dc.b4DragOut(e, outEvts);
21534                     dc.onDragOut(e, outEvts);
21535                 }
21536
21537                 if (enterEvts.length) {
21538                     dc.onDragEnter(e, enterEvts);
21539                 }
21540
21541                 if (overEvts.length) {
21542                     dc.b4DragOver(e, overEvts);
21543                     dc.onDragOver(e, overEvts);
21544                 }
21545
21546                 if (dropEvts.length) {
21547                     dc.b4DragDrop(e, dropEvts);
21548                     dc.onDragDrop(e, dropEvts);
21549                 }
21550
21551             } else {
21552                 // fire dragout events
21553                 var len = 0;
21554                 for (i=0, len=outEvts.length; i<len; ++i) {
21555                     dc.b4DragOut(e, outEvts[i].id);
21556                     dc.onDragOut(e, outEvts[i].id);
21557                 }
21558
21559                 // fire enter events
21560                 for (i=0,len=enterEvts.length; i<len; ++i) {
21561                     // dc.b4DragEnter(e, oDD.id);
21562                     dc.onDragEnter(e, enterEvts[i].id);
21563                 }
21564
21565                 // fire over events
21566                 for (i=0,len=overEvts.length; i<len; ++i) {
21567                     dc.b4DragOver(e, overEvts[i].id);
21568                     dc.onDragOver(e, overEvts[i].id);
21569                 }
21570
21571                 // fire drop events
21572                 for (i=0, len=dropEvts.length; i<len; ++i) {
21573                     dc.b4DragDrop(e, dropEvts[i].id);
21574                     dc.onDragDrop(e, dropEvts[i].id);
21575                 }
21576
21577             }
21578
21579             // notify about a drop that did not find a target
21580             if (isDrop && !dropEvts.length) {
21581                 dc.onInvalidDrop(e);
21582             }
21583
21584         },
21585
21586         /**
21587          * Helper function for getting the best match from the list of drag
21588          * and drop objects returned by the drag and drop events when we are
21589          * in INTERSECT mode.  It returns either the first object that the
21590          * cursor is over, or the object that has the greatest overlap with
21591          * the dragged element.
21592          * @method getBestMatch
21593          * @param  {DragDrop[]} dds The array of drag and drop objects
21594          * targeted
21595          * @return {DragDrop}       The best single match
21596          * @static
21597          */
21598         getBestMatch: function(dds) {
21599             var winner = null;
21600             // Return null if the input is not what we expect
21601             //if (!dds || !dds.length || dds.length == 0) {
21602                // winner = null;
21603             // If there is only one item, it wins
21604             //} else if (dds.length == 1) {
21605
21606             var len = dds.length;
21607
21608             if (len == 1) {
21609                 winner = dds[0];
21610             } else {
21611                 // Loop through the targeted items
21612                 for (var i=0; i<len; ++i) {
21613                     var dd = dds[i];
21614                     // If the cursor is over the object, it wins.  If the
21615                     // cursor is over multiple matches, the first one we come
21616                     // to wins.
21617                     if (dd.cursorIsOver) {
21618                         winner = dd;
21619                         break;
21620                     // Otherwise the object with the most overlap wins
21621                     } else {
21622                         if (!winner ||
21623                             winner.overlap.getArea() < dd.overlap.getArea()) {
21624                             winner = dd;
21625                         }
21626                     }
21627                 }
21628             }
21629
21630             return winner;
21631         },
21632
21633         /**
21634          * Refreshes the cache of the top-left and bottom-right points of the
21635          * drag and drop objects in the specified group(s).  This is in the
21636          * format that is stored in the drag and drop instance, so typical
21637          * usage is:
21638          * <code>
21639          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21640          * </code>
21641          * Alternatively:
21642          * <code>
21643          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21644          * </code>
21645          * @TODO this really should be an indexed array.  Alternatively this
21646          * method could accept both.
21647          * @method refreshCache
21648          * @param {Object} groups an associative array of groups to refresh
21649          * @static
21650          */
21651         refreshCache: function(groups) {
21652             for (var sGroup in groups) {
21653                 if ("string" != typeof sGroup) {
21654                     continue;
21655                 }
21656                 for (var i in this.ids[sGroup]) {
21657                     var oDD = this.ids[sGroup][i];
21658
21659                     if (this.isTypeOfDD(oDD)) {
21660                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21661                         var loc = this.getLocation(oDD);
21662                         if (loc) {
21663                             this.locationCache[oDD.id] = loc;
21664                         } else {
21665                             delete this.locationCache[oDD.id];
21666                             // this will unregister the drag and drop object if
21667                             // the element is not in a usable state
21668                             // oDD.unreg();
21669                         }
21670                     }
21671                 }
21672             }
21673         },
21674
21675         /**
21676          * This checks to make sure an element exists and is in the DOM.  The
21677          * main purpose is to handle cases where innerHTML is used to remove
21678          * drag and drop objects from the DOM.  IE provides an 'unspecified
21679          * error' when trying to access the offsetParent of such an element
21680          * @method verifyEl
21681          * @param {HTMLElement} el the element to check
21682          * @return {boolean} true if the element looks usable
21683          * @static
21684          */
21685         verifyEl: function(el) {
21686             if (el) {
21687                 var parent;
21688                 if(Roo.isIE){
21689                     try{
21690                         parent = el.offsetParent;
21691                     }catch(e){}
21692                 }else{
21693                     parent = el.offsetParent;
21694                 }
21695                 if (parent) {
21696                     return true;
21697                 }
21698             }
21699
21700             return false;
21701         },
21702
21703         /**
21704          * Returns a Region object containing the drag and drop element's position
21705          * and size, including the padding configured for it
21706          * @method getLocation
21707          * @param {DragDrop} oDD the drag and drop object to get the
21708          *                       location for
21709          * @return {Roo.lib.Region} a Region object representing the total area
21710          *                             the element occupies, including any padding
21711          *                             the instance is configured for.
21712          * @static
21713          */
21714         getLocation: function(oDD) {
21715             if (! this.isTypeOfDD(oDD)) {
21716                 return null;
21717             }
21718
21719             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21720
21721             try {
21722                 pos= Roo.lib.Dom.getXY(el);
21723             } catch (e) { }
21724
21725             if (!pos) {
21726                 return null;
21727             }
21728
21729             x1 = pos[0];
21730             x2 = x1 + el.offsetWidth;
21731             y1 = pos[1];
21732             y2 = y1 + el.offsetHeight;
21733
21734             t = y1 - oDD.padding[0];
21735             r = x2 + oDD.padding[1];
21736             b = y2 + oDD.padding[2];
21737             l = x1 - oDD.padding[3];
21738
21739             return new Roo.lib.Region( t, r, b, l );
21740         },
21741
21742         /**
21743          * Checks the cursor location to see if it over the target
21744          * @method isOverTarget
21745          * @param {Roo.lib.Point} pt The point to evaluate
21746          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21747          * @return {boolean} true if the mouse is over the target
21748          * @private
21749          * @static
21750          */
21751         isOverTarget: function(pt, oTarget, intersect) {
21752             // use cache if available
21753             var loc = this.locationCache[oTarget.id];
21754             if (!loc || !this.useCache) {
21755                 loc = this.getLocation(oTarget);
21756                 this.locationCache[oTarget.id] = loc;
21757
21758             }
21759
21760             if (!loc) {
21761                 return false;
21762             }
21763
21764             oTarget.cursorIsOver = loc.contains( pt );
21765
21766             // DragDrop is using this as a sanity check for the initial mousedown
21767             // in this case we are done.  In POINT mode, if the drag obj has no
21768             // contraints, we are also done. Otherwise we need to evaluate the
21769             // location of the target as related to the actual location of the
21770             // dragged element.
21771             var dc = this.dragCurrent;
21772             if (!dc || !dc.getTargetCoord ||
21773                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21774                 return oTarget.cursorIsOver;
21775             }
21776
21777             oTarget.overlap = null;
21778
21779             // Get the current location of the drag element, this is the
21780             // location of the mouse event less the delta that represents
21781             // where the original mousedown happened on the element.  We
21782             // need to consider constraints and ticks as well.
21783             var pos = dc.getTargetCoord(pt.x, pt.y);
21784
21785             var el = dc.getDragEl();
21786             var curRegion = new Roo.lib.Region( pos.y,
21787                                                    pos.x + el.offsetWidth,
21788                                                    pos.y + el.offsetHeight,
21789                                                    pos.x );
21790
21791             var overlap = curRegion.intersect(loc);
21792
21793             if (overlap) {
21794                 oTarget.overlap = overlap;
21795                 return (intersect) ? true : oTarget.cursorIsOver;
21796             } else {
21797                 return false;
21798             }
21799         },
21800
21801         /**
21802          * unload event handler
21803          * @method _onUnload
21804          * @private
21805          * @static
21806          */
21807         _onUnload: function(e, me) {
21808             Roo.dd.DragDropMgr.unregAll();
21809         },
21810
21811         /**
21812          * Cleans up the drag and drop events and objects.
21813          * @method unregAll
21814          * @private
21815          * @static
21816          */
21817         unregAll: function() {
21818
21819             if (this.dragCurrent) {
21820                 this.stopDrag();
21821                 this.dragCurrent = null;
21822             }
21823
21824             this._execOnAll("unreg", []);
21825
21826             for (i in this.elementCache) {
21827                 delete this.elementCache[i];
21828             }
21829
21830             this.elementCache = {};
21831             this.ids = {};
21832         },
21833
21834         /**
21835          * A cache of DOM elements
21836          * @property elementCache
21837          * @private
21838          * @static
21839          */
21840         elementCache: {},
21841
21842         /**
21843          * Get the wrapper for the DOM element specified
21844          * @method getElWrapper
21845          * @param {String} id the id of the element to get
21846          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21847          * @private
21848          * @deprecated This wrapper isn't that useful
21849          * @static
21850          */
21851         getElWrapper: function(id) {
21852             var oWrapper = this.elementCache[id];
21853             if (!oWrapper || !oWrapper.el) {
21854                 oWrapper = this.elementCache[id] =
21855                     new this.ElementWrapper(Roo.getDom(id));
21856             }
21857             return oWrapper;
21858         },
21859
21860         /**
21861          * Returns the actual DOM element
21862          * @method getElement
21863          * @param {String} id the id of the elment to get
21864          * @return {Object} The element
21865          * @deprecated use Roo.getDom instead
21866          * @static
21867          */
21868         getElement: function(id) {
21869             return Roo.getDom(id);
21870         },
21871
21872         /**
21873          * Returns the style property for the DOM element (i.e.,
21874          * document.getElById(id).style)
21875          * @method getCss
21876          * @param {String} id the id of the elment to get
21877          * @return {Object} The style property of the element
21878          * @deprecated use Roo.getDom instead
21879          * @static
21880          */
21881         getCss: function(id) {
21882             var el = Roo.getDom(id);
21883             return (el) ? el.style : null;
21884         },
21885
21886         /**
21887          * Inner class for cached elements
21888          * @class DragDropMgr.ElementWrapper
21889          * @for DragDropMgr
21890          * @private
21891          * @deprecated
21892          */
21893         ElementWrapper: function(el) {
21894                 /**
21895                  * The element
21896                  * @property el
21897                  */
21898                 this.el = el || null;
21899                 /**
21900                  * The element id
21901                  * @property id
21902                  */
21903                 this.id = this.el && el.id;
21904                 /**
21905                  * A reference to the style property
21906                  * @property css
21907                  */
21908                 this.css = this.el && el.style;
21909             },
21910
21911         /**
21912          * Returns the X position of an html element
21913          * @method getPosX
21914          * @param el the element for which to get the position
21915          * @return {int} the X coordinate
21916          * @for DragDropMgr
21917          * @deprecated use Roo.lib.Dom.getX instead
21918          * @static
21919          */
21920         getPosX: function(el) {
21921             return Roo.lib.Dom.getX(el);
21922         },
21923
21924         /**
21925          * Returns the Y position of an html element
21926          * @method getPosY
21927          * @param el the element for which to get the position
21928          * @return {int} the Y coordinate
21929          * @deprecated use Roo.lib.Dom.getY instead
21930          * @static
21931          */
21932         getPosY: function(el) {
21933             return Roo.lib.Dom.getY(el);
21934         },
21935
21936         /**
21937          * Swap two nodes.  In IE, we use the native method, for others we
21938          * emulate the IE behavior
21939          * @method swapNode
21940          * @param n1 the first node to swap
21941          * @param n2 the other node to swap
21942          * @static
21943          */
21944         swapNode: function(n1, n2) {
21945             if (n1.swapNode) {
21946                 n1.swapNode(n2);
21947             } else {
21948                 var p = n2.parentNode;
21949                 var s = n2.nextSibling;
21950
21951                 if (s == n1) {
21952                     p.insertBefore(n1, n2);
21953                 } else if (n2 == n1.nextSibling) {
21954                     p.insertBefore(n2, n1);
21955                 } else {
21956                     n1.parentNode.replaceChild(n2, n1);
21957                     p.insertBefore(n1, s);
21958                 }
21959             }
21960         },
21961
21962         /**
21963          * Returns the current scroll position
21964          * @method getScroll
21965          * @private
21966          * @static
21967          */
21968         getScroll: function () {
21969             var t, l, dde=document.documentElement, db=document.body;
21970             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21971                 t = dde.scrollTop;
21972                 l = dde.scrollLeft;
21973             } else if (db) {
21974                 t = db.scrollTop;
21975                 l = db.scrollLeft;
21976             } else {
21977
21978             }
21979             return { top: t, left: l };
21980         },
21981
21982         /**
21983          * Returns the specified element style property
21984          * @method getStyle
21985          * @param {HTMLElement} el          the element
21986          * @param {string}      styleProp   the style property
21987          * @return {string} The value of the style property
21988          * @deprecated use Roo.lib.Dom.getStyle
21989          * @static
21990          */
21991         getStyle: function(el, styleProp) {
21992             return Roo.fly(el).getStyle(styleProp);
21993         },
21994
21995         /**
21996          * Gets the scrollTop
21997          * @method getScrollTop
21998          * @return {int} the document's scrollTop
21999          * @static
22000          */
22001         getScrollTop: function () { return this.getScroll().top; },
22002
22003         /**
22004          * Gets the scrollLeft
22005          * @method getScrollLeft
22006          * @return {int} the document's scrollTop
22007          * @static
22008          */
22009         getScrollLeft: function () { return this.getScroll().left; },
22010
22011         /**
22012          * Sets the x/y position of an element to the location of the
22013          * target element.
22014          * @method moveToEl
22015          * @param {HTMLElement} moveEl      The element to move
22016          * @param {HTMLElement} targetEl    The position reference element
22017          * @static
22018          */
22019         moveToEl: function (moveEl, targetEl) {
22020             var aCoord = Roo.lib.Dom.getXY(targetEl);
22021             Roo.lib.Dom.setXY(moveEl, aCoord);
22022         },
22023
22024         /**
22025          * Numeric array sort function
22026          * @method numericSort
22027          * @static
22028          */
22029         numericSort: function(a, b) { return (a - b); },
22030
22031         /**
22032          * Internal counter
22033          * @property _timeoutCount
22034          * @private
22035          * @static
22036          */
22037         _timeoutCount: 0,
22038
22039         /**
22040          * Trying to make the load order less important.  Without this we get
22041          * an error if this file is loaded before the Event Utility.
22042          * @method _addListeners
22043          * @private
22044          * @static
22045          */
22046         _addListeners: function() {
22047             var DDM = Roo.dd.DDM;
22048             if ( Roo.lib.Event && document ) {
22049                 DDM._onLoad();
22050             } else {
22051                 if (DDM._timeoutCount > 2000) {
22052                 } else {
22053                     setTimeout(DDM._addListeners, 10);
22054                     if (document && document.body) {
22055                         DDM._timeoutCount += 1;
22056                     }
22057                 }
22058             }
22059         },
22060
22061         /**
22062          * Recursively searches the immediate parent and all child nodes for
22063          * the handle element in order to determine wheter or not it was
22064          * clicked.
22065          * @method handleWasClicked
22066          * @param node the html element to inspect
22067          * @static
22068          */
22069         handleWasClicked: function(node, id) {
22070             if (this.isHandle(id, node.id)) {
22071                 return true;
22072             } else {
22073                 // check to see if this is a text node child of the one we want
22074                 var p = node.parentNode;
22075
22076                 while (p) {
22077                     if (this.isHandle(id, p.id)) {
22078                         return true;
22079                     } else {
22080                         p = p.parentNode;
22081                     }
22082                 }
22083             }
22084
22085             return false;
22086         }
22087
22088     };
22089
22090 }();
22091
22092 // shorter alias, save a few bytes
22093 Roo.dd.DDM = Roo.dd.DragDropMgr;
22094 Roo.dd.DDM._addListeners();
22095
22096 }/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106
22107 /**
22108  * @class Roo.dd.DD
22109  * A DragDrop implementation where the linked element follows the
22110  * mouse cursor during a drag.
22111  * @extends Roo.dd.DragDrop
22112  * @constructor
22113  * @param {String} id the id of the linked element
22114  * @param {String} sGroup the group of related DragDrop items
22115  * @param {object} config an object containing configurable attributes
22116  *                Valid properties for DD:
22117  *                    scroll
22118  */
22119 Roo.dd.DD = function(id, sGroup, config) {
22120     if (id) {
22121         this.init(id, sGroup, config);
22122     }
22123 };
22124
22125 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22126
22127     /**
22128      * When set to true, the utility automatically tries to scroll the browser
22129      * window wehn a drag and drop element is dragged near the viewport boundary.
22130      * Defaults to true.
22131      * @property scroll
22132      * @type boolean
22133      */
22134     scroll: true,
22135
22136     /**
22137      * Sets the pointer offset to the distance between the linked element's top
22138      * left corner and the location the element was clicked
22139      * @method autoOffset
22140      * @param {int} iPageX the X coordinate of the click
22141      * @param {int} iPageY the Y coordinate of the click
22142      */
22143     autoOffset: function(iPageX, iPageY) {
22144         var x = iPageX - this.startPageX;
22145         var y = iPageY - this.startPageY;
22146         this.setDelta(x, y);
22147     },
22148
22149     /**
22150      * Sets the pointer offset.  You can call this directly to force the
22151      * offset to be in a particular location (e.g., pass in 0,0 to set it
22152      * to the center of the object)
22153      * @method setDelta
22154      * @param {int} iDeltaX the distance from the left
22155      * @param {int} iDeltaY the distance from the top
22156      */
22157     setDelta: function(iDeltaX, iDeltaY) {
22158         this.deltaX = iDeltaX;
22159         this.deltaY = iDeltaY;
22160     },
22161
22162     /**
22163      * Sets the drag element to the location of the mousedown or click event,
22164      * maintaining the cursor location relative to the location on the element
22165      * that was clicked.  Override this if you want to place the element in a
22166      * location other than where the cursor is.
22167      * @method setDragElPos
22168      * @param {int} iPageX the X coordinate of the mousedown or drag event
22169      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22170      */
22171     setDragElPos: function(iPageX, iPageY) {
22172         // the first time we do this, we are going to check to make sure
22173         // the element has css positioning
22174
22175         var el = this.getDragEl();
22176         this.alignElWithMouse(el, iPageX, iPageY);
22177     },
22178
22179     /**
22180      * Sets the element to the location of the mousedown or click event,
22181      * maintaining the cursor location relative to the location on the element
22182      * that was clicked.  Override this if you want to place the element in a
22183      * location other than where the cursor is.
22184      * @method alignElWithMouse
22185      * @param {HTMLElement} el the element to move
22186      * @param {int} iPageX the X coordinate of the mousedown or drag event
22187      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22188      */
22189     alignElWithMouse: function(el, iPageX, iPageY) {
22190         var oCoord = this.getTargetCoord(iPageX, iPageY);
22191         var fly = el.dom ? el : Roo.fly(el);
22192         if (!this.deltaSetXY) {
22193             var aCoord = [oCoord.x, oCoord.y];
22194             fly.setXY(aCoord);
22195             var newLeft = fly.getLeft(true);
22196             var newTop  = fly.getTop(true);
22197             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22198         } else {
22199             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22200         }
22201
22202         this.cachePosition(oCoord.x, oCoord.y);
22203         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22204         return oCoord;
22205     },
22206
22207     /**
22208      * Saves the most recent position so that we can reset the constraints and
22209      * tick marks on-demand.  We need to know this so that we can calculate the
22210      * number of pixels the element is offset from its original position.
22211      * @method cachePosition
22212      * @param iPageX the current x position (optional, this just makes it so we
22213      * don't have to look it up again)
22214      * @param iPageY the current y position (optional, this just makes it so we
22215      * don't have to look it up again)
22216      */
22217     cachePosition: function(iPageX, iPageY) {
22218         if (iPageX) {
22219             this.lastPageX = iPageX;
22220             this.lastPageY = iPageY;
22221         } else {
22222             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22223             this.lastPageX = aCoord[0];
22224             this.lastPageY = aCoord[1];
22225         }
22226     },
22227
22228     /**
22229      * Auto-scroll the window if the dragged object has been moved beyond the
22230      * visible window boundary.
22231      * @method autoScroll
22232      * @param {int} x the drag element's x position
22233      * @param {int} y the drag element's y position
22234      * @param {int} h the height of the drag element
22235      * @param {int} w the width of the drag element
22236      * @private
22237      */
22238     autoScroll: function(x, y, h, w) {
22239
22240         if (this.scroll) {
22241             // The client height
22242             var clientH = Roo.lib.Dom.getViewWidth();
22243
22244             // The client width
22245             var clientW = Roo.lib.Dom.getViewHeight();
22246
22247             // The amt scrolled down
22248             var st = this.DDM.getScrollTop();
22249
22250             // The amt scrolled right
22251             var sl = this.DDM.getScrollLeft();
22252
22253             // Location of the bottom of the element
22254             var bot = h + y;
22255
22256             // Location of the right of the element
22257             var right = w + x;
22258
22259             // The distance from the cursor to the bottom of the visible area,
22260             // adjusted so that we don't scroll if the cursor is beyond the
22261             // element drag constraints
22262             var toBot = (clientH + st - y - this.deltaY);
22263
22264             // The distance from the cursor to the right of the visible area
22265             var toRight = (clientW + sl - x - this.deltaX);
22266
22267
22268             // How close to the edge the cursor must be before we scroll
22269             // var thresh = (document.all) ? 100 : 40;
22270             var thresh = 40;
22271
22272             // How many pixels to scroll per autoscroll op.  This helps to reduce
22273             // clunky scrolling. IE is more sensitive about this ... it needs this
22274             // value to be higher.
22275             var scrAmt = (document.all) ? 80 : 30;
22276
22277             // Scroll down if we are near the bottom of the visible page and the
22278             // obj extends below the crease
22279             if ( bot > clientH && toBot < thresh ) {
22280                 window.scrollTo(sl, st + scrAmt);
22281             }
22282
22283             // Scroll up if the window is scrolled down and the top of the object
22284             // goes above the top border
22285             if ( y < st && st > 0 && y - st < thresh ) {
22286                 window.scrollTo(sl, st - scrAmt);
22287             }
22288
22289             // Scroll right if the obj is beyond the right border and the cursor is
22290             // near the border.
22291             if ( right > clientW && toRight < thresh ) {
22292                 window.scrollTo(sl + scrAmt, st);
22293             }
22294
22295             // Scroll left if the window has been scrolled to the right and the obj
22296             // extends past the left border
22297             if ( x < sl && sl > 0 && x - sl < thresh ) {
22298                 window.scrollTo(sl - scrAmt, st);
22299             }
22300         }
22301     },
22302
22303     /**
22304      * Finds the location the element should be placed if we want to move
22305      * it to where the mouse location less the click offset would place us.
22306      * @method getTargetCoord
22307      * @param {int} iPageX the X coordinate of the click
22308      * @param {int} iPageY the Y coordinate of the click
22309      * @return an object that contains the coordinates (Object.x and Object.y)
22310      * @private
22311      */
22312     getTargetCoord: function(iPageX, iPageY) {
22313
22314
22315         var x = iPageX - this.deltaX;
22316         var y = iPageY - this.deltaY;
22317
22318         if (this.constrainX) {
22319             if (x < this.minX) { x = this.minX; }
22320             if (x > this.maxX) { x = this.maxX; }
22321         }
22322
22323         if (this.constrainY) {
22324             if (y < this.minY) { y = this.minY; }
22325             if (y > this.maxY) { y = this.maxY; }
22326         }
22327
22328         x = this.getTick(x, this.xTicks);
22329         y = this.getTick(y, this.yTicks);
22330
22331
22332         return {x:x, y:y};
22333     },
22334
22335     /*
22336      * Sets up config options specific to this class. Overrides
22337      * Roo.dd.DragDrop, but all versions of this method through the
22338      * inheritance chain are called
22339      */
22340     applyConfig: function() {
22341         Roo.dd.DD.superclass.applyConfig.call(this);
22342         this.scroll = (this.config.scroll !== false);
22343     },
22344
22345     /*
22346      * Event that fires prior to the onMouseDown event.  Overrides
22347      * Roo.dd.DragDrop.
22348      */
22349     b4MouseDown: function(e) {
22350         // this.resetConstraints();
22351         this.autoOffset(e.getPageX(),
22352                             e.getPageY());
22353     },
22354
22355     /*
22356      * Event that fires prior to the onDrag event.  Overrides
22357      * Roo.dd.DragDrop.
22358      */
22359     b4Drag: function(e) {
22360         this.setDragElPos(e.getPageX(),
22361                             e.getPageY());
22362     },
22363
22364     toString: function() {
22365         return ("DD " + this.id);
22366     }
22367
22368     //////////////////////////////////////////////////////////////////////////
22369     // Debugging ygDragDrop events that can be overridden
22370     //////////////////////////////////////////////////////////////////////////
22371     /*
22372     startDrag: function(x, y) {
22373     },
22374
22375     onDrag: function(e) {
22376     },
22377
22378     onDragEnter: function(e, id) {
22379     },
22380
22381     onDragOver: function(e, id) {
22382     },
22383
22384     onDragOut: function(e, id) {
22385     },
22386
22387     onDragDrop: function(e, id) {
22388     },
22389
22390     endDrag: function(e) {
22391     }
22392
22393     */
22394
22395 });/*
22396  * Based on:
22397  * Ext JS Library 1.1.1
22398  * Copyright(c) 2006-2007, Ext JS, LLC.
22399  *
22400  * Originally Released Under LGPL - original licence link has changed is not relivant.
22401  *
22402  * Fork - LGPL
22403  * <script type="text/javascript">
22404  */
22405
22406 /**
22407  * @class Roo.dd.DDProxy
22408  * A DragDrop implementation that inserts an empty, bordered div into
22409  * the document that follows the cursor during drag operations.  At the time of
22410  * the click, the frame div is resized to the dimensions of the linked html
22411  * element, and moved to the exact location of the linked element.
22412  *
22413  * References to the "frame" element refer to the single proxy element that
22414  * was created to be dragged in place of all DDProxy elements on the
22415  * page.
22416  *
22417  * @extends Roo.dd.DD
22418  * @constructor
22419  * @param {String} id the id of the linked html element
22420  * @param {String} sGroup the group of related DragDrop objects
22421  * @param {object} config an object containing configurable attributes
22422  *                Valid properties for DDProxy in addition to those in DragDrop:
22423  *                   resizeFrame, centerFrame, dragElId
22424  */
22425 Roo.dd.DDProxy = function(id, sGroup, config) {
22426     if (id) {
22427         this.init(id, sGroup, config);
22428         this.initFrame();
22429     }
22430 };
22431
22432 /**
22433  * The default drag frame div id
22434  * @property Roo.dd.DDProxy.dragElId
22435  * @type String
22436  * @static
22437  */
22438 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22439
22440 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22441
22442     /**
22443      * By default we resize the drag frame to be the same size as the element
22444      * we want to drag (this is to get the frame effect).  We can turn it off
22445      * if we want a different behavior.
22446      * @property resizeFrame
22447      * @type boolean
22448      */
22449     resizeFrame: true,
22450
22451     /**
22452      * By default the frame is positioned exactly where the drag element is, so
22453      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22454      * you do not have constraints on the obj is to have the drag frame centered
22455      * around the cursor.  Set centerFrame to true for this effect.
22456      * @property centerFrame
22457      * @type boolean
22458      */
22459     centerFrame: false,
22460
22461     /**
22462      * Creates the proxy element if it does not yet exist
22463      * @method createFrame
22464      */
22465     createFrame: function() {
22466         var self = this;
22467         var body = document.body;
22468
22469         if (!body || !body.firstChild) {
22470             setTimeout( function() { self.createFrame(); }, 50 );
22471             return;
22472         }
22473
22474         var div = this.getDragEl();
22475
22476         if (!div) {
22477             div    = document.createElement("div");
22478             div.id = this.dragElId;
22479             var s  = div.style;
22480
22481             s.position   = "absolute";
22482             s.visibility = "hidden";
22483             s.cursor     = "move";
22484             s.border     = "2px solid #aaa";
22485             s.zIndex     = 999;
22486
22487             // appendChild can blow up IE if invoked prior to the window load event
22488             // while rendering a table.  It is possible there are other scenarios
22489             // that would cause this to happen as well.
22490             body.insertBefore(div, body.firstChild);
22491         }
22492     },
22493
22494     /**
22495      * Initialization for the drag frame element.  Must be called in the
22496      * constructor of all subclasses
22497      * @method initFrame
22498      */
22499     initFrame: function() {
22500         this.createFrame();
22501     },
22502
22503     applyConfig: function() {
22504         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22505
22506         this.resizeFrame = (this.config.resizeFrame !== false);
22507         this.centerFrame = (this.config.centerFrame);
22508         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22509     },
22510
22511     /**
22512      * Resizes the drag frame to the dimensions of the clicked object, positions
22513      * it over the object, and finally displays it
22514      * @method showFrame
22515      * @param {int} iPageX X click position
22516      * @param {int} iPageY Y click position
22517      * @private
22518      */
22519     showFrame: function(iPageX, iPageY) {
22520         var el = this.getEl();
22521         var dragEl = this.getDragEl();
22522         var s = dragEl.style;
22523
22524         this._resizeProxy();
22525
22526         if (this.centerFrame) {
22527             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22528                            Math.round(parseInt(s.height, 10)/2) );
22529         }
22530
22531         this.setDragElPos(iPageX, iPageY);
22532
22533         Roo.fly(dragEl).show();
22534     },
22535
22536     /**
22537      * The proxy is automatically resized to the dimensions of the linked
22538      * element when a drag is initiated, unless resizeFrame is set to false
22539      * @method _resizeProxy
22540      * @private
22541      */
22542     _resizeProxy: function() {
22543         if (this.resizeFrame) {
22544             var el = this.getEl();
22545             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22546         }
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     b4MouseDown: function(e) {
22551         var x = e.getPageX();
22552         var y = e.getPageY();
22553         this.autoOffset(x, y);
22554         this.setDragElPos(x, y);
22555     },
22556
22557     // overrides Roo.dd.DragDrop
22558     b4StartDrag: function(x, y) {
22559         // show the drag frame
22560         this.showFrame(x, y);
22561     },
22562
22563     // overrides Roo.dd.DragDrop
22564     b4EndDrag: function(e) {
22565         Roo.fly(this.getDragEl()).hide();
22566     },
22567
22568     // overrides Roo.dd.DragDrop
22569     // By default we try to move the element to the last location of the frame.
22570     // This is so that the default behavior mirrors that of Roo.dd.DD.
22571     endDrag: function(e) {
22572
22573         var lel = this.getEl();
22574         var del = this.getDragEl();
22575
22576         // Show the drag frame briefly so we can get its position
22577         del.style.visibility = "";
22578
22579         this.beforeMove();
22580         // Hide the linked element before the move to get around a Safari
22581         // rendering bug.
22582         lel.style.visibility = "hidden";
22583         Roo.dd.DDM.moveToEl(lel, del);
22584         del.style.visibility = "hidden";
22585         lel.style.visibility = "";
22586
22587         this.afterDrag();
22588     },
22589
22590     beforeMove : function(){
22591
22592     },
22593
22594     afterDrag : function(){
22595
22596     },
22597
22598     toString: function() {
22599         return ("DDProxy " + this.id);
22600     }
22601
22602 });
22603 /*
22604  * Based on:
22605  * Ext JS Library 1.1.1
22606  * Copyright(c) 2006-2007, Ext JS, LLC.
22607  *
22608  * Originally Released Under LGPL - original licence link has changed is not relivant.
22609  *
22610  * Fork - LGPL
22611  * <script type="text/javascript">
22612  */
22613
22614  /**
22615  * @class Roo.dd.DDTarget
22616  * A DragDrop implementation that does not move, but can be a drop
22617  * target.  You would get the same result by simply omitting implementation
22618  * for the event callbacks, but this way we reduce the processing cost of the
22619  * event listener and the callbacks.
22620  * @extends Roo.dd.DragDrop
22621  * @constructor
22622  * @param {String} id the id of the element that is a drop target
22623  * @param {String} sGroup the group of related DragDrop objects
22624  * @param {object} config an object containing configurable attributes
22625  *                 Valid properties for DDTarget in addition to those in
22626  *                 DragDrop:
22627  *                    none
22628  */
22629 Roo.dd.DDTarget = function(id, sGroup, config) {
22630     if (id) {
22631         this.initTarget(id, sGroup, config);
22632     }
22633     if (config && (config.listeners || config.events)) { 
22634         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22635             listeners : config.listeners || {}, 
22636             events : config.events || {} 
22637         });    
22638     }
22639 };
22640
22641 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22642 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22643     toString: function() {
22644         return ("DDTarget " + this.id);
22645     }
22646 });
22647 /*
22648  * Based on:
22649  * Ext JS Library 1.1.1
22650  * Copyright(c) 2006-2007, Ext JS, LLC.
22651  *
22652  * Originally Released Under LGPL - original licence link has changed is not relivant.
22653  *
22654  * Fork - LGPL
22655  * <script type="text/javascript">
22656  */
22657  
22658
22659 /**
22660  * @class Roo.dd.ScrollManager
22661  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22662  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22663  * @static
22664  */
22665 Roo.dd.ScrollManager = function(){
22666     var ddm = Roo.dd.DragDropMgr;
22667     var els = {};
22668     var dragEl = null;
22669     var proc = {};
22670     
22671     
22672     
22673     var onStop = function(e){
22674         dragEl = null;
22675         clearProc();
22676     };
22677     
22678     var triggerRefresh = function(){
22679         if(ddm.dragCurrent){
22680              ddm.refreshCache(ddm.dragCurrent.groups);
22681         }
22682     };
22683     
22684     var doScroll = function(){
22685         if(ddm.dragCurrent){
22686             var dds = Roo.dd.ScrollManager;
22687             if(!dds.animate){
22688                 if(proc.el.scroll(proc.dir, dds.increment)){
22689                     triggerRefresh();
22690                 }
22691             }else{
22692                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22693             }
22694         }
22695     };
22696     
22697     var clearProc = function(){
22698         if(proc.id){
22699             clearInterval(proc.id);
22700         }
22701         proc.id = 0;
22702         proc.el = null;
22703         proc.dir = "";
22704     };
22705     
22706     var startProc = function(el, dir){
22707          Roo.log('scroll startproc');
22708         clearProc();
22709         proc.el = el;
22710         proc.dir = dir;
22711         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22712     };
22713     
22714     var onFire = function(e, isDrop){
22715        
22716         if(isDrop || !ddm.dragCurrent){ return; }
22717         var dds = Roo.dd.ScrollManager;
22718         if(!dragEl || dragEl != ddm.dragCurrent){
22719             dragEl = ddm.dragCurrent;
22720             // refresh regions on drag start
22721             dds.refreshCache();
22722         }
22723         
22724         var xy = Roo.lib.Event.getXY(e);
22725         var pt = new Roo.lib.Point(xy[0], xy[1]);
22726         for(var id in els){
22727             var el = els[id], r = el._region;
22728             if(r && r.contains(pt) && el.isScrollable()){
22729                 if(r.bottom - pt.y <= dds.thresh){
22730                     if(proc.el != el){
22731                         startProc(el, "down");
22732                     }
22733                     return;
22734                 }else if(r.right - pt.x <= dds.thresh){
22735                     if(proc.el != el){
22736                         startProc(el, "left");
22737                     }
22738                     return;
22739                 }else if(pt.y - r.top <= dds.thresh){
22740                     if(proc.el != el){
22741                         startProc(el, "up");
22742                     }
22743                     return;
22744                 }else if(pt.x - r.left <= dds.thresh){
22745                     if(proc.el != el){
22746                         startProc(el, "right");
22747                     }
22748                     return;
22749                 }
22750             }
22751         }
22752         clearProc();
22753     };
22754     
22755     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22756     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22757     
22758     return {
22759         /**
22760          * Registers new overflow element(s) to auto scroll
22761          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22762          */
22763         register : function(el){
22764             if(el instanceof Array){
22765                 for(var i = 0, len = el.length; i < len; i++) {
22766                         this.register(el[i]);
22767                 }
22768             }else{
22769                 el = Roo.get(el);
22770                 els[el.id] = el;
22771             }
22772             Roo.dd.ScrollManager.els = els;
22773         },
22774         
22775         /**
22776          * Unregisters overflow element(s) so they are no longer scrolled
22777          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22778          */
22779         unregister : function(el){
22780             if(el instanceof Array){
22781                 for(var i = 0, len = el.length; i < len; i++) {
22782                         this.unregister(el[i]);
22783                 }
22784             }else{
22785                 el = Roo.get(el);
22786                 delete els[el.id];
22787             }
22788         },
22789         
22790         /**
22791          * The number of pixels from the edge of a container the pointer needs to be to 
22792          * trigger scrolling (defaults to 25)
22793          * @type Number
22794          */
22795         thresh : 25,
22796         
22797         /**
22798          * The number of pixels to scroll in each scroll increment (defaults to 50)
22799          * @type Number
22800          */
22801         increment : 100,
22802         
22803         /**
22804          * The frequency of scrolls in milliseconds (defaults to 500)
22805          * @type Number
22806          */
22807         frequency : 500,
22808         
22809         /**
22810          * True to animate the scroll (defaults to true)
22811          * @type Boolean
22812          */
22813         animate: true,
22814         
22815         /**
22816          * The animation duration in seconds - 
22817          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22818          * @type Number
22819          */
22820         animDuration: .4,
22821         
22822         /**
22823          * Manually trigger a cache refresh.
22824          */
22825         refreshCache : function(){
22826             for(var id in els){
22827                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22828                     els[id]._region = els[id].getRegion();
22829                 }
22830             }
22831         }
22832     };
22833 }();/*
22834  * Based on:
22835  * Ext JS Library 1.1.1
22836  * Copyright(c) 2006-2007, Ext JS, LLC.
22837  *
22838  * Originally Released Under LGPL - original licence link has changed is not relivant.
22839  *
22840  * Fork - LGPL
22841  * <script type="text/javascript">
22842  */
22843  
22844
22845 /**
22846  * @class Roo.dd.Registry
22847  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22848  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22849  * @static
22850  */
22851 Roo.dd.Registry = function(){
22852     var elements = {}; 
22853     var handles = {}; 
22854     var autoIdSeed = 0;
22855
22856     var getId = function(el, autogen){
22857         if(typeof el == "string"){
22858             return el;
22859         }
22860         var id = el.id;
22861         if(!id && autogen !== false){
22862             id = "roodd-" + (++autoIdSeed);
22863             el.id = id;
22864         }
22865         return id;
22866     };
22867     
22868     return {
22869     /**
22870      * Register a drag drop element
22871      * @param {String|HTMLElement} element The id or DOM node to register
22872      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22873      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22874      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22875      * populated in the data object (if applicable):
22876      * <pre>
22877 Value      Description<br />
22878 ---------  ------------------------------------------<br />
22879 handles    Array of DOM nodes that trigger dragging<br />
22880            for the element being registered<br />
22881 isHandle   True if the element passed in triggers<br />
22882            dragging itself, else false
22883 </pre>
22884      */
22885         register : function(el, data){
22886             data = data || {};
22887             if(typeof el == "string"){
22888                 el = document.getElementById(el);
22889             }
22890             data.ddel = el;
22891             elements[getId(el)] = data;
22892             if(data.isHandle !== false){
22893                 handles[data.ddel.id] = data;
22894             }
22895             if(data.handles){
22896                 var hs = data.handles;
22897                 for(var i = 0, len = hs.length; i < len; i++){
22898                         handles[getId(hs[i])] = data;
22899                 }
22900             }
22901         },
22902
22903     /**
22904      * Unregister a drag drop element
22905      * @param {String|HTMLElement}  element The id or DOM node to unregister
22906      */
22907         unregister : function(el){
22908             var id = getId(el, false);
22909             var data = elements[id];
22910             if(data){
22911                 delete elements[id];
22912                 if(data.handles){
22913                     var hs = data.handles;
22914                     for(var i = 0, len = hs.length; i < len; i++){
22915                         delete handles[getId(hs[i], false)];
22916                     }
22917                 }
22918             }
22919         },
22920
22921     /**
22922      * Returns the handle registered for a DOM Node by id
22923      * @param {String|HTMLElement} id The DOM node or id to look up
22924      * @return {Object} handle The custom handle data
22925      */
22926         getHandle : function(id){
22927             if(typeof id != "string"){ // must be element?
22928                 id = id.id;
22929             }
22930             return handles[id];
22931         },
22932
22933     /**
22934      * Returns the handle that is registered for the DOM node that is the target of the event
22935      * @param {Event} e The event
22936      * @return {Object} handle The custom handle data
22937      */
22938         getHandleFromEvent : function(e){
22939             var t = Roo.lib.Event.getTarget(e);
22940             return t ? handles[t.id] : null;
22941         },
22942
22943     /**
22944      * Returns a custom data object that is registered for a DOM node by id
22945      * @param {String|HTMLElement} id The DOM node or id to look up
22946      * @return {Object} data The custom data
22947      */
22948         getTarget : function(id){
22949             if(typeof id != "string"){ // must be element?
22950                 id = id.id;
22951             }
22952             return elements[id];
22953         },
22954
22955     /**
22956      * Returns a custom data object that is registered for the DOM node that is the target of the event
22957      * @param {Event} e The event
22958      * @return {Object} data The custom data
22959      */
22960         getTargetFromEvent : function(e){
22961             var t = Roo.lib.Event.getTarget(e);
22962             return t ? elements[t.id] || handles[t.id] : null;
22963         }
22964     };
22965 }();/*
22966  * Based on:
22967  * Ext JS Library 1.1.1
22968  * Copyright(c) 2006-2007, Ext JS, LLC.
22969  *
22970  * Originally Released Under LGPL - original licence link has changed is not relivant.
22971  *
22972  * Fork - LGPL
22973  * <script type="text/javascript">
22974  */
22975  
22976
22977 /**
22978  * @class Roo.dd.StatusProxy
22979  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22980  * default drag proxy used by all Roo.dd components.
22981  * @constructor
22982  * @param {Object} config
22983  */
22984 Roo.dd.StatusProxy = function(config){
22985     Roo.apply(this, config);
22986     this.id = this.id || Roo.id();
22987     this.el = new Roo.Layer({
22988         dh: {
22989             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22990                 {tag: "div", cls: "x-dd-drop-icon"},
22991                 {tag: "div", cls: "x-dd-drag-ghost"}
22992             ]
22993         }, 
22994         shadow: !config || config.shadow !== false
22995     });
22996     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22997     this.dropStatus = this.dropNotAllowed;
22998 };
22999
23000 Roo.dd.StatusProxy.prototype = {
23001     /**
23002      * @cfg {String} dropAllowed
23003      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23004      */
23005     dropAllowed : "x-dd-drop-ok",
23006     /**
23007      * @cfg {String} dropNotAllowed
23008      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23009      */
23010     dropNotAllowed : "x-dd-drop-nodrop",
23011
23012     /**
23013      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23014      * over the current target element.
23015      * @param {String} cssClass The css class for the new drop status indicator image
23016      */
23017     setStatus : function(cssClass){
23018         cssClass = cssClass || this.dropNotAllowed;
23019         if(this.dropStatus != cssClass){
23020             this.el.replaceClass(this.dropStatus, cssClass);
23021             this.dropStatus = cssClass;
23022         }
23023     },
23024
23025     /**
23026      * Resets the status indicator to the default dropNotAllowed value
23027      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23028      */
23029     reset : function(clearGhost){
23030         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23031         this.dropStatus = this.dropNotAllowed;
23032         if(clearGhost){
23033             this.ghost.update("");
23034         }
23035     },
23036
23037     /**
23038      * Updates the contents of the ghost element
23039      * @param {String} html The html that will replace the current innerHTML of the ghost element
23040      */
23041     update : function(html){
23042         if(typeof html == "string"){
23043             this.ghost.update(html);
23044         }else{
23045             this.ghost.update("");
23046             html.style.margin = "0";
23047             this.ghost.dom.appendChild(html);
23048         }
23049         // ensure float = none set?? cant remember why though.
23050         var el = this.ghost.dom.firstChild;
23051                 if(el){
23052                         Roo.fly(el).setStyle('float', 'none');
23053                 }
23054     },
23055     
23056     /**
23057      * Returns the underlying proxy {@link Roo.Layer}
23058      * @return {Roo.Layer} el
23059     */
23060     getEl : function(){
23061         return this.el;
23062     },
23063
23064     /**
23065      * Returns the ghost element
23066      * @return {Roo.Element} el
23067      */
23068     getGhost : function(){
23069         return this.ghost;
23070     },
23071
23072     /**
23073      * Hides the proxy
23074      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23075      */
23076     hide : function(clear){
23077         this.el.hide();
23078         if(clear){
23079             this.reset(true);
23080         }
23081     },
23082
23083     /**
23084      * Stops the repair animation if it's currently running
23085      */
23086     stop : function(){
23087         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23088             this.anim.stop();
23089         }
23090     },
23091
23092     /**
23093      * Displays this proxy
23094      */
23095     show : function(){
23096         this.el.show();
23097     },
23098
23099     /**
23100      * Force the Layer to sync its shadow and shim positions to the element
23101      */
23102     sync : function(){
23103         this.el.sync();
23104     },
23105
23106     /**
23107      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23108      * invalid drop operation by the item being dragged.
23109      * @param {Array} xy The XY position of the element ([x, y])
23110      * @param {Function} callback The function to call after the repair is complete
23111      * @param {Object} scope The scope in which to execute the callback
23112      */
23113     repair : function(xy, callback, scope){
23114         this.callback = callback;
23115         this.scope = scope;
23116         if(xy && this.animRepair !== false){
23117             this.el.addClass("x-dd-drag-repair");
23118             this.el.hideUnders(true);
23119             this.anim = this.el.shift({
23120                 duration: this.repairDuration || .5,
23121                 easing: 'easeOut',
23122                 xy: xy,
23123                 stopFx: true,
23124                 callback: this.afterRepair,
23125                 scope: this
23126             });
23127         }else{
23128             this.afterRepair();
23129         }
23130     },
23131
23132     // private
23133     afterRepair : function(){
23134         this.hide(true);
23135         if(typeof this.callback == "function"){
23136             this.callback.call(this.scope || this);
23137         }
23138         this.callback = null;
23139         this.scope = null;
23140     }
23141 };/*
23142  * Based on:
23143  * Ext JS Library 1.1.1
23144  * Copyright(c) 2006-2007, Ext JS, LLC.
23145  *
23146  * Originally Released Under LGPL - original licence link has changed is not relivant.
23147  *
23148  * Fork - LGPL
23149  * <script type="text/javascript">
23150  */
23151
23152 /**
23153  * @class Roo.dd.DragSource
23154  * @extends Roo.dd.DDProxy
23155  * A simple class that provides the basic implementation needed to make any element draggable.
23156  * @constructor
23157  * @param {String/HTMLElement/Element} el The container element
23158  * @param {Object} config
23159  */
23160 Roo.dd.DragSource = function(el, config){
23161     this.el = Roo.get(el);
23162     this.dragData = {};
23163     
23164     Roo.apply(this, config);
23165     
23166     if(!this.proxy){
23167         this.proxy = new Roo.dd.StatusProxy();
23168     }
23169
23170     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23171           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23172     
23173     this.dragging = false;
23174 };
23175
23176 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23177     /**
23178      * @cfg {String} dropAllowed
23179      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23180      */
23181     dropAllowed : "x-dd-drop-ok",
23182     /**
23183      * @cfg {String} dropNotAllowed
23184      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23185      */
23186     dropNotAllowed : "x-dd-drop-nodrop",
23187
23188     /**
23189      * Returns the data object associated with this drag source
23190      * @return {Object} data An object containing arbitrary data
23191      */
23192     getDragData : function(e){
23193         return this.dragData;
23194     },
23195
23196     // private
23197     onDragEnter : function(e, id){
23198         var target = Roo.dd.DragDropMgr.getDDById(id);
23199         this.cachedTarget = target;
23200         if(this.beforeDragEnter(target, e, id) !== false){
23201             if(target.isNotifyTarget){
23202                 var status = target.notifyEnter(this, e, this.dragData);
23203                 this.proxy.setStatus(status);
23204             }else{
23205                 this.proxy.setStatus(this.dropAllowed);
23206             }
23207             
23208             if(this.afterDragEnter){
23209                 /**
23210                  * An empty function by default, but provided so that you can perform a custom action
23211                  * when the dragged item enters the drop target by providing an implementation.
23212                  * @param {Roo.dd.DragDrop} target The drop target
23213                  * @param {Event} e The event object
23214                  * @param {String} id The id of the dragged element
23215                  * @method afterDragEnter
23216                  */
23217                 this.afterDragEnter(target, e, id);
23218             }
23219         }
23220     },
23221
23222     /**
23223      * An empty function by default, but provided so that you can perform a custom action
23224      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23225      * @param {Roo.dd.DragDrop} target The drop target
23226      * @param {Event} e The event object
23227      * @param {String} id The id of the dragged element
23228      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23229      */
23230     beforeDragEnter : function(target, e, id){
23231         return true;
23232     },
23233
23234     // private
23235     alignElWithMouse: function() {
23236         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23237         this.proxy.sync();
23238     },
23239
23240     // private
23241     onDragOver : function(e, id){
23242         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23243         if(this.beforeDragOver(target, e, id) !== false){
23244             if(target.isNotifyTarget){
23245                 var status = target.notifyOver(this, e, this.dragData);
23246                 this.proxy.setStatus(status);
23247             }
23248
23249             if(this.afterDragOver){
23250                 /**
23251                  * An empty function by default, but provided so that you can perform a custom action
23252                  * while the dragged item is over the drop target by providing an implementation.
23253                  * @param {Roo.dd.DragDrop} target The drop target
23254                  * @param {Event} e The event object
23255                  * @param {String} id The id of the dragged element
23256                  * @method afterDragOver
23257                  */
23258                 this.afterDragOver(target, e, id);
23259             }
23260         }
23261     },
23262
23263     /**
23264      * An empty function by default, but provided so that you can perform a custom action
23265      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23266      * @param {Roo.dd.DragDrop} target The drop target
23267      * @param {Event} e The event object
23268      * @param {String} id The id of the dragged element
23269      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23270      */
23271     beforeDragOver : function(target, e, id){
23272         return true;
23273     },
23274
23275     // private
23276     onDragOut : function(e, id){
23277         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23278         if(this.beforeDragOut(target, e, id) !== false){
23279             if(target.isNotifyTarget){
23280                 target.notifyOut(this, e, this.dragData);
23281             }
23282             this.proxy.reset();
23283             if(this.afterDragOut){
23284                 /**
23285                  * An empty function by default, but provided so that you can perform a custom action
23286                  * after the dragged item is dragged out of the target without dropping.
23287                  * @param {Roo.dd.DragDrop} target The drop target
23288                  * @param {Event} e The event object
23289                  * @param {String} id The id of the dragged element
23290                  * @method afterDragOut
23291                  */
23292                 this.afterDragOut(target, e, id);
23293             }
23294         }
23295         this.cachedTarget = null;
23296     },
23297
23298     /**
23299      * An empty function by default, but provided so that you can perform a custom action before the dragged
23300      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23301      * @param {Roo.dd.DragDrop} target The drop target
23302      * @param {Event} e The event object
23303      * @param {String} id The id of the dragged element
23304      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23305      */
23306     beforeDragOut : function(target, e, id){
23307         return true;
23308     },
23309     
23310     // private
23311     onDragDrop : function(e, id){
23312         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23313         if(this.beforeDragDrop(target, e, id) !== false){
23314             if(target.isNotifyTarget){
23315                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23316                     this.onValidDrop(target, e, id);
23317                 }else{
23318                     this.onInvalidDrop(target, e, id);
23319                 }
23320             }else{
23321                 this.onValidDrop(target, e, id);
23322             }
23323             
23324             if(this.afterDragDrop){
23325                 /**
23326                  * An empty function by default, but provided so that you can perform a custom action
23327                  * after a valid drag drop has occurred by providing an implementation.
23328                  * @param {Roo.dd.DragDrop} target The drop target
23329                  * @param {Event} e The event object
23330                  * @param {String} id The id of the dropped element
23331                  * @method afterDragDrop
23332                  */
23333                 this.afterDragDrop(target, e, id);
23334             }
23335         }
23336         delete this.cachedTarget;
23337     },
23338
23339     /**
23340      * An empty function by default, but provided so that you can perform a custom action before the dragged
23341      * item is dropped onto the target and optionally cancel the onDragDrop.
23342      * @param {Roo.dd.DragDrop} target The drop target
23343      * @param {Event} e The event object
23344      * @param {String} id The id of the dragged element
23345      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23346      */
23347     beforeDragDrop : function(target, e, id){
23348         return true;
23349     },
23350
23351     // private
23352     onValidDrop : function(target, e, id){
23353         this.hideProxy();
23354         if(this.afterValidDrop){
23355             /**
23356              * An empty function by default, but provided so that you can perform a custom action
23357              * after a valid drop has occurred by providing an implementation.
23358              * @param {Object} target The target DD 
23359              * @param {Event} e The event object
23360              * @param {String} id The id of the dropped element
23361              * @method afterInvalidDrop
23362              */
23363             this.afterValidDrop(target, e, id);
23364         }
23365     },
23366
23367     // private
23368     getRepairXY : function(e, data){
23369         return this.el.getXY();  
23370     },
23371
23372     // private
23373     onInvalidDrop : function(target, e, id){
23374         this.beforeInvalidDrop(target, e, id);
23375         if(this.cachedTarget){
23376             if(this.cachedTarget.isNotifyTarget){
23377                 this.cachedTarget.notifyOut(this, e, this.dragData);
23378             }
23379             this.cacheTarget = null;
23380         }
23381         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23382
23383         if(this.afterInvalidDrop){
23384             /**
23385              * An empty function by default, but provided so that you can perform a custom action
23386              * after an invalid drop has occurred by providing an implementation.
23387              * @param {Event} e The event object
23388              * @param {String} id The id of the dropped element
23389              * @method afterInvalidDrop
23390              */
23391             this.afterInvalidDrop(e, id);
23392         }
23393     },
23394
23395     // private
23396     afterRepair : function(){
23397         if(Roo.enableFx){
23398             this.el.highlight(this.hlColor || "c3daf9");
23399         }
23400         this.dragging = false;
23401     },
23402
23403     /**
23404      * An empty function by default, but provided so that you can perform a custom action after an invalid
23405      * drop has occurred.
23406      * @param {Roo.dd.DragDrop} target The drop target
23407      * @param {Event} e The event object
23408      * @param {String} id The id of the dragged element
23409      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23410      */
23411     beforeInvalidDrop : function(target, e, id){
23412         return true;
23413     },
23414
23415     // private
23416     handleMouseDown : function(e){
23417         if(this.dragging) {
23418             return;
23419         }
23420         var data = this.getDragData(e);
23421         if(data && this.onBeforeDrag(data, e) !== false){
23422             this.dragData = data;
23423             this.proxy.stop();
23424             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23425         } 
23426     },
23427
23428     /**
23429      * An empty function by default, but provided so that you can perform a custom action before the initial
23430      * drag event begins and optionally cancel it.
23431      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23432      * @param {Event} e The event object
23433      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23434      */
23435     onBeforeDrag : function(data, e){
23436         return true;
23437     },
23438
23439     /**
23440      * An empty function by default, but provided so that you can perform a custom action once the initial
23441      * drag event has begun.  The drag cannot be canceled from this function.
23442      * @param {Number} x The x position of the click on the dragged object
23443      * @param {Number} y The y position of the click on the dragged object
23444      */
23445     onStartDrag : Roo.emptyFn,
23446
23447     // private - YUI override
23448     startDrag : function(x, y){
23449         this.proxy.reset();
23450         this.dragging = true;
23451         this.proxy.update("");
23452         this.onInitDrag(x, y);
23453         this.proxy.show();
23454     },
23455
23456     // private
23457     onInitDrag : function(x, y){
23458         var clone = this.el.dom.cloneNode(true);
23459         clone.id = Roo.id(); // prevent duplicate ids
23460         this.proxy.update(clone);
23461         this.onStartDrag(x, y);
23462         return true;
23463     },
23464
23465     /**
23466      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23467      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23468      */
23469     getProxy : function(){
23470         return this.proxy;  
23471     },
23472
23473     /**
23474      * Hides the drag source's {@link Roo.dd.StatusProxy}
23475      */
23476     hideProxy : function(){
23477         this.proxy.hide();  
23478         this.proxy.reset(true);
23479         this.dragging = false;
23480     },
23481
23482     // private
23483     triggerCacheRefresh : function(){
23484         Roo.dd.DDM.refreshCache(this.groups);
23485     },
23486
23487     // private - override to prevent hiding
23488     b4EndDrag: function(e) {
23489     },
23490
23491     // private - override to prevent moving
23492     endDrag : function(e){
23493         this.onEndDrag(this.dragData, e);
23494     },
23495
23496     // private
23497     onEndDrag : function(data, e){
23498     },
23499     
23500     // private - pin to cursor
23501     autoOffset : function(x, y) {
23502         this.setDelta(-12, -20);
23503     }    
23504 });/*
23505  * Based on:
23506  * Ext JS Library 1.1.1
23507  * Copyright(c) 2006-2007, Ext JS, LLC.
23508  *
23509  * Originally Released Under LGPL - original licence link has changed is not relivant.
23510  *
23511  * Fork - LGPL
23512  * <script type="text/javascript">
23513  */
23514
23515
23516 /**
23517  * @class Roo.dd.DropTarget
23518  * @extends Roo.dd.DDTarget
23519  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23520  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23521  * @constructor
23522  * @param {String/HTMLElement/Element} el The container element
23523  * @param {Object} config
23524  */
23525 Roo.dd.DropTarget = function(el, config){
23526     this.el = Roo.get(el);
23527     
23528     var listeners = false; ;
23529     if (config && config.listeners) {
23530         listeners= config.listeners;
23531         delete config.listeners;
23532     }
23533     Roo.apply(this, config);
23534     
23535     if(this.containerScroll){
23536         Roo.dd.ScrollManager.register(this.el);
23537     }
23538     this.addEvents( {
23539          /**
23540          * @scope Roo.dd.DropTarget
23541          */
23542          
23543          /**
23544          * @event enter
23545          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23546          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23547          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23548          * 
23549          * IMPORTANT : it should set  this.valid to true|false
23550          * 
23551          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23552          * @param {Event} e The event
23553          * @param {Object} data An object containing arbitrary data supplied by the drag source
23554          */
23555         "enter" : true,
23556         
23557          /**
23558          * @event over
23559          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23560          * This method will be called on every mouse movement while the drag source is over the drop target.
23561          * This default implementation simply returns the dropAllowed config value.
23562          * 
23563          * IMPORTANT : it should set  this.valid to true|false
23564          * 
23565          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23566          * @param {Event} e The event
23567          * @param {Object} data An object containing arbitrary data supplied by the drag source
23568          
23569          */
23570         "over" : true,
23571         /**
23572          * @event out
23573          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23574          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23575          * overClass (if any) from the drop element.
23576          * 
23577          * 
23578          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23579          * @param {Event} e The event
23580          * @param {Object} data An object containing arbitrary data supplied by the drag source
23581          */
23582          "out" : true,
23583          
23584         /**
23585          * @event drop
23586          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23587          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23588          * implementation that does something to process the drop event and returns true so that the drag source's
23589          * repair action does not run.
23590          * 
23591          * IMPORTANT : it should set this.success
23592          * 
23593          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23594          * @param {Event} e The event
23595          * @param {Object} data An object containing arbitrary data supplied by the drag source
23596         */
23597          "drop" : true
23598     });
23599             
23600      
23601     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23602         this.el.dom, 
23603         this.ddGroup || this.group,
23604         {
23605             isTarget: true,
23606             listeners : listeners || {} 
23607            
23608         
23609         }
23610     );
23611
23612 };
23613
23614 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23615     /**
23616      * @cfg {String} overClass
23617      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23618      */
23619      /**
23620      * @cfg {String} ddGroup
23621      * The drag drop group to handle drop events for
23622      */
23623      
23624     /**
23625      * @cfg {String} dropAllowed
23626      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23627      */
23628     dropAllowed : "x-dd-drop-ok",
23629     /**
23630      * @cfg {String} dropNotAllowed
23631      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23632      */
23633     dropNotAllowed : "x-dd-drop-nodrop",
23634     /**
23635      * @cfg {boolean} success
23636      * set this after drop listener.. 
23637      */
23638     success : false,
23639     /**
23640      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23641      * if the drop point is valid for over/enter..
23642      */
23643     valid : false,
23644     // private
23645     isTarget : true,
23646
23647     // private
23648     isNotifyTarget : true,
23649     
23650     /**
23651      * @hide
23652      */
23653     notifyEnter : function(dd, e, data)
23654     {
23655         this.valid = true;
23656         this.fireEvent('enter', dd, e, data);
23657         if(this.overClass){
23658             this.el.addClass(this.overClass);
23659         }
23660         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23661             this.valid ? this.dropAllowed : this.dropNotAllowed
23662         );
23663     },
23664
23665     /**
23666      * @hide
23667      */
23668     notifyOver : function(dd, e, data)
23669     {
23670         this.valid = true;
23671         this.fireEvent('over', dd, e, data);
23672         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23673             this.valid ? this.dropAllowed : this.dropNotAllowed
23674         );
23675     },
23676
23677     /**
23678      * @hide
23679      */
23680     notifyOut : function(dd, e, data)
23681     {
23682         this.fireEvent('out', dd, e, data);
23683         if(this.overClass){
23684             this.el.removeClass(this.overClass);
23685         }
23686     },
23687
23688     /**
23689      * @hide
23690      */
23691     notifyDrop : function(dd, e, data)
23692     {
23693         this.success = false;
23694         this.fireEvent('drop', dd, e, data);
23695         return this.success;
23696     }
23697 });/*
23698  * Based on:
23699  * Ext JS Library 1.1.1
23700  * Copyright(c) 2006-2007, Ext JS, LLC.
23701  *
23702  * Originally Released Under LGPL - original licence link has changed is not relivant.
23703  *
23704  * Fork - LGPL
23705  * <script type="text/javascript">
23706  */
23707
23708
23709 /**
23710  * @class Roo.dd.DragZone
23711  * @extends Roo.dd.DragSource
23712  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23713  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23714  * @constructor
23715  * @param {String/HTMLElement/Element} el The container element
23716  * @param {Object} config
23717  */
23718 Roo.dd.DragZone = function(el, config){
23719     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23720     if(this.containerScroll){
23721         Roo.dd.ScrollManager.register(this.el);
23722     }
23723 };
23724
23725 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23726     /**
23727      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23728      * for auto scrolling during drag operations.
23729      */
23730     /**
23731      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23732      * method after a failed drop (defaults to "c3daf9" - light blue)
23733      */
23734
23735     /**
23736      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23737      * for a valid target to drag based on the mouse down. Override this method
23738      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23739      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23740      * @param {EventObject} e The mouse down event
23741      * @return {Object} The dragData
23742      */
23743     getDragData : function(e){
23744         return Roo.dd.Registry.getHandleFromEvent(e);
23745     },
23746     
23747     /**
23748      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23749      * this.dragData.ddel
23750      * @param {Number} x The x position of the click on the dragged object
23751      * @param {Number} y The y position of the click on the dragged object
23752      * @return {Boolean} true to continue the drag, false to cancel
23753      */
23754     onInitDrag : function(x, y){
23755         this.proxy.update(this.dragData.ddel.cloneNode(true));
23756         this.onStartDrag(x, y);
23757         return true;
23758     },
23759     
23760     /**
23761      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23762      */
23763     afterRepair : function(){
23764         if(Roo.enableFx){
23765             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23766         }
23767         this.dragging = false;
23768     },
23769
23770     /**
23771      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23772      * the XY of this.dragData.ddel
23773      * @param {EventObject} e The mouse up event
23774      * @return {Array} The xy location (e.g. [100, 200])
23775      */
23776     getRepairXY : function(e){
23777         return Roo.Element.fly(this.dragData.ddel).getXY();  
23778     }
23779 });/*
23780  * Based on:
23781  * Ext JS Library 1.1.1
23782  * Copyright(c) 2006-2007, Ext JS, LLC.
23783  *
23784  * Originally Released Under LGPL - original licence link has changed is not relivant.
23785  *
23786  * Fork - LGPL
23787  * <script type="text/javascript">
23788  */
23789 /**
23790  * @class Roo.dd.DropZone
23791  * @extends Roo.dd.DropTarget
23792  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23793  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23794  * @constructor
23795  * @param {String/HTMLElement/Element} el The container element
23796  * @param {Object} config
23797  */
23798 Roo.dd.DropZone = function(el, config){
23799     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23800 };
23801
23802 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23803     /**
23804      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23805      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23806      * provide your own custom lookup.
23807      * @param {Event} e The event
23808      * @return {Object} data The custom data
23809      */
23810     getTargetFromEvent : function(e){
23811         return Roo.dd.Registry.getTargetFromEvent(e);
23812     },
23813
23814     /**
23815      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23816      * that it has registered.  This method has no default implementation and should be overridden to provide
23817      * node-specific processing if necessary.
23818      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23819      * {@link #getTargetFromEvent} for this node)
23820      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23821      * @param {Event} e The event
23822      * @param {Object} data An object containing arbitrary data supplied by the drag source
23823      */
23824     onNodeEnter : function(n, dd, e, data){
23825         
23826     },
23827
23828     /**
23829      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23830      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23831      * overridden to provide the proper feedback.
23832      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23833      * {@link #getTargetFromEvent} for this node)
23834      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23835      * @param {Event} e The event
23836      * @param {Object} data An object containing arbitrary data supplied by the drag source
23837      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23838      * underlying {@link Roo.dd.StatusProxy} can be updated
23839      */
23840     onNodeOver : function(n, dd, e, data){
23841         return this.dropAllowed;
23842     },
23843
23844     /**
23845      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23846      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23847      * node-specific processing if necessary.
23848      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23849      * {@link #getTargetFromEvent} for this node)
23850      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23851      * @param {Event} e The event
23852      * @param {Object} data An object containing arbitrary data supplied by the drag source
23853      */
23854     onNodeOut : function(n, dd, e, data){
23855         
23856     },
23857
23858     /**
23859      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23860      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23861      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23862      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23863      * {@link #getTargetFromEvent} for this node)
23864      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23865      * @param {Event} e The event
23866      * @param {Object} data An object containing arbitrary data supplied by the drag source
23867      * @return {Boolean} True if the drop was valid, else false
23868      */
23869     onNodeDrop : function(n, dd, e, data){
23870         return false;
23871     },
23872
23873     /**
23874      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23875      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23876      * it should be overridden to provide the proper feedback if necessary.
23877      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23878      * @param {Event} e The event
23879      * @param {Object} data An object containing arbitrary data supplied by the drag source
23880      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23881      * underlying {@link Roo.dd.StatusProxy} can be updated
23882      */
23883     onContainerOver : function(dd, e, data){
23884         return this.dropNotAllowed;
23885     },
23886
23887     /**
23888      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23889      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23890      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23891      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23892      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23893      * @param {Event} e The event
23894      * @param {Object} data An object containing arbitrary data supplied by the drag source
23895      * @return {Boolean} True if the drop was valid, else false
23896      */
23897     onContainerDrop : function(dd, e, data){
23898         return false;
23899     },
23900
23901     /**
23902      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23903      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23904      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23905      * you should override this method and provide a custom implementation.
23906      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23907      * @param {Event} e The event
23908      * @param {Object} data An object containing arbitrary data supplied by the drag source
23909      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23910      * underlying {@link Roo.dd.StatusProxy} can be updated
23911      */
23912     notifyEnter : function(dd, e, data){
23913         return this.dropNotAllowed;
23914     },
23915
23916     /**
23917      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23918      * This method will be called on every mouse movement while the drag source is over the drop zone.
23919      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23920      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23921      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23922      * registered node, it will call {@link #onContainerOver}.
23923      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23924      * @param {Event} e The event
23925      * @param {Object} data An object containing arbitrary data supplied by the drag source
23926      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23927      * underlying {@link Roo.dd.StatusProxy} can be updated
23928      */
23929     notifyOver : function(dd, e, data){
23930         var n = this.getTargetFromEvent(e);
23931         if(!n){ // not over valid drop target
23932             if(this.lastOverNode){
23933                 this.onNodeOut(this.lastOverNode, dd, e, data);
23934                 this.lastOverNode = null;
23935             }
23936             return this.onContainerOver(dd, e, data);
23937         }
23938         if(this.lastOverNode != n){
23939             if(this.lastOverNode){
23940                 this.onNodeOut(this.lastOverNode, dd, e, data);
23941             }
23942             this.onNodeEnter(n, dd, e, data);
23943             this.lastOverNode = n;
23944         }
23945         return this.onNodeOver(n, dd, e, data);
23946     },
23947
23948     /**
23949      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23950      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23951      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23952      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23953      * @param {Event} e The event
23954      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23955      */
23956     notifyOut : function(dd, e, data){
23957         if(this.lastOverNode){
23958             this.onNodeOut(this.lastOverNode, dd, e, data);
23959             this.lastOverNode = null;
23960         }
23961     },
23962
23963     /**
23964      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23965      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23966      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23967      * otherwise it will call {@link #onContainerDrop}.
23968      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23969      * @param {Event} e The event
23970      * @param {Object} data An object containing arbitrary data supplied by the drag source
23971      * @return {Boolean} True if the drop was valid, else false
23972      */
23973     notifyDrop : function(dd, e, data){
23974         if(this.lastOverNode){
23975             this.onNodeOut(this.lastOverNode, dd, e, data);
23976             this.lastOverNode = null;
23977         }
23978         var n = this.getTargetFromEvent(e);
23979         return n ?
23980             this.onNodeDrop(n, dd, e, data) :
23981             this.onContainerDrop(dd, e, data);
23982     },
23983
23984     // private
23985     triggerCacheRefresh : function(){
23986         Roo.dd.DDM.refreshCache(this.groups);
23987     }  
23988 });/*
23989  * Based on:
23990  * Ext JS Library 1.1.1
23991  * Copyright(c) 2006-2007, Ext JS, LLC.
23992  *
23993  * Originally Released Under LGPL - original licence link has changed is not relivant.
23994  *
23995  * Fork - LGPL
23996  * <script type="text/javascript">
23997  */
23998
23999
24000 /**
24001  * @class Roo.data.SortTypes
24002  * @static
24003  * Defines the default sorting (casting?) comparison functions used when sorting data.
24004  */
24005 Roo.data.SortTypes = {
24006     /**
24007      * Default sort that does nothing
24008      * @param {Mixed} s The value being converted
24009      * @return {Mixed} The comparison value
24010      */
24011     none : function(s){
24012         return s;
24013     },
24014     
24015     /**
24016      * The regular expression used to strip tags
24017      * @type {RegExp}
24018      * @property
24019      */
24020     stripTagsRE : /<\/?[^>]+>/gi,
24021     
24022     /**
24023      * Strips all HTML tags to sort on text only
24024      * @param {Mixed} s The value being converted
24025      * @return {String} The comparison value
24026      */
24027     asText : function(s){
24028         return String(s).replace(this.stripTagsRE, "");
24029     },
24030     
24031     /**
24032      * Strips all HTML tags to sort on text only - Case insensitive
24033      * @param {Mixed} s The value being converted
24034      * @return {String} The comparison value
24035      */
24036     asUCText : function(s){
24037         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24038     },
24039     
24040     /**
24041      * Case insensitive string
24042      * @param {Mixed} s The value being converted
24043      * @return {String} The comparison value
24044      */
24045     asUCString : function(s) {
24046         return String(s).toUpperCase();
24047     },
24048     
24049     /**
24050      * Date sorting
24051      * @param {Mixed} s The value being converted
24052      * @return {Number} The comparison value
24053      */
24054     asDate : function(s) {
24055         if(!s){
24056             return 0;
24057         }
24058         if(s instanceof Date){
24059             return s.getTime();
24060         }
24061         return Date.parse(String(s));
24062     },
24063     
24064     /**
24065      * Float sorting
24066      * @param {Mixed} s The value being converted
24067      * @return {Float} The comparison value
24068      */
24069     asFloat : function(s) {
24070         var val = parseFloat(String(s).replace(/,/g, ""));
24071         if(isNaN(val)) {
24072             val = 0;
24073         }
24074         return val;
24075     },
24076     
24077     /**
24078      * Integer sorting
24079      * @param {Mixed} s The value being converted
24080      * @return {Number} The comparison value
24081      */
24082     asInt : function(s) {
24083         var val = parseInt(String(s).replace(/,/g, ""));
24084         if(isNaN(val)) {
24085             val = 0;
24086         }
24087         return val;
24088     }
24089 };/*
24090  * Based on:
24091  * Ext JS Library 1.1.1
24092  * Copyright(c) 2006-2007, Ext JS, LLC.
24093  *
24094  * Originally Released Under LGPL - original licence link has changed is not relivant.
24095  *
24096  * Fork - LGPL
24097  * <script type="text/javascript">
24098  */
24099
24100 /**
24101 * @class Roo.data.Record
24102  * Instances of this class encapsulate both record <em>definition</em> information, and record
24103  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24104  * to access Records cached in an {@link Roo.data.Store} object.<br>
24105  * <p>
24106  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24107  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24108  * objects.<br>
24109  * <p>
24110  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24111  * @constructor
24112  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24113  * {@link #create}. The parameters are the same.
24114  * @param {Array} data An associative Array of data values keyed by the field name.
24115  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24116  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24117  * not specified an integer id is generated.
24118  */
24119 Roo.data.Record = function(data, id){
24120     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24121     this.data = data;
24122 };
24123
24124 /**
24125  * Generate a constructor for a specific record layout.
24126  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24127  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24128  * Each field definition object may contain the following properties: <ul>
24129  * <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,
24130  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24131  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24132  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24133  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24134  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24135  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24136  * this may be omitted.</p></li>
24137  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24138  * <ul><li>auto (Default, implies no conversion)</li>
24139  * <li>string</li>
24140  * <li>int</li>
24141  * <li>float</li>
24142  * <li>boolean</li>
24143  * <li>date</li></ul></p></li>
24144  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24145  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24146  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24147  * by the Reader into an object that will be stored in the Record. It is passed the
24148  * following parameters:<ul>
24149  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24150  * </ul></p></li>
24151  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24152  * </ul>
24153  * <br>usage:<br><pre><code>
24154 var TopicRecord = Roo.data.Record.create(
24155     {name: 'title', mapping: 'topic_title'},
24156     {name: 'author', mapping: 'username'},
24157     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24158     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24159     {name: 'lastPoster', mapping: 'user2'},
24160     {name: 'excerpt', mapping: 'post_text'}
24161 );
24162
24163 var myNewRecord = new TopicRecord({
24164     title: 'Do my job please',
24165     author: 'noobie',
24166     totalPosts: 1,
24167     lastPost: new Date(),
24168     lastPoster: 'Animal',
24169     excerpt: 'No way dude!'
24170 });
24171 myStore.add(myNewRecord);
24172 </code></pre>
24173  * @method create
24174  * @static
24175  */
24176 Roo.data.Record.create = function(o){
24177     var f = function(){
24178         f.superclass.constructor.apply(this, arguments);
24179     };
24180     Roo.extend(f, Roo.data.Record);
24181     var p = f.prototype;
24182     p.fields = new Roo.util.MixedCollection(false, function(field){
24183         return field.name;
24184     });
24185     for(var i = 0, len = o.length; i < len; i++){
24186         p.fields.add(new Roo.data.Field(o[i]));
24187     }
24188     f.getField = function(name){
24189         return p.fields.get(name);  
24190     };
24191     return f;
24192 };
24193
24194 Roo.data.Record.AUTO_ID = 1000;
24195 Roo.data.Record.EDIT = 'edit';
24196 Roo.data.Record.REJECT = 'reject';
24197 Roo.data.Record.COMMIT = 'commit';
24198
24199 Roo.data.Record.prototype = {
24200     /**
24201      * Readonly flag - true if this record has been modified.
24202      * @type Boolean
24203      */
24204     dirty : false,
24205     editing : false,
24206     error: null,
24207     modified: null,
24208
24209     // private
24210     join : function(store){
24211         this.store = store;
24212     },
24213
24214     /**
24215      * Set the named field to the specified value.
24216      * @param {String} name The name of the field to set.
24217      * @param {Object} value The value to set the field to.
24218      */
24219     set : function(name, value){
24220         if(this.data[name] == value){
24221             return;
24222         }
24223         this.dirty = true;
24224         if(!this.modified){
24225             this.modified = {};
24226         }
24227         if(typeof this.modified[name] == 'undefined'){
24228             this.modified[name] = this.data[name];
24229         }
24230         this.data[name] = value;
24231         if(!this.editing && this.store){
24232             this.store.afterEdit(this);
24233         }       
24234     },
24235
24236     /**
24237      * Get the value of the named field.
24238      * @param {String} name The name of the field to get the value of.
24239      * @return {Object} The value of the field.
24240      */
24241     get : function(name){
24242         return this.data[name]; 
24243     },
24244
24245     // private
24246     beginEdit : function(){
24247         this.editing = true;
24248         this.modified = {}; 
24249     },
24250
24251     // private
24252     cancelEdit : function(){
24253         this.editing = false;
24254         delete this.modified;
24255     },
24256
24257     // private
24258     endEdit : function(){
24259         this.editing = false;
24260         if(this.dirty && this.store){
24261             this.store.afterEdit(this);
24262         }
24263     },
24264
24265     /**
24266      * Usually called by the {@link Roo.data.Store} which owns the Record.
24267      * Rejects all changes made to the Record since either creation, or the last commit operation.
24268      * Modified fields are reverted to their original values.
24269      * <p>
24270      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24271      * of reject operations.
24272      */
24273     reject : function(){
24274         var m = this.modified;
24275         for(var n in m){
24276             if(typeof m[n] != "function"){
24277                 this.data[n] = m[n];
24278             }
24279         }
24280         this.dirty = false;
24281         delete this.modified;
24282         this.editing = false;
24283         if(this.store){
24284             this.store.afterReject(this);
24285         }
24286     },
24287
24288     /**
24289      * Usually called by the {@link Roo.data.Store} which owns the Record.
24290      * Commits all changes made to the Record since either creation, or the last commit operation.
24291      * <p>
24292      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24293      * of commit operations.
24294      */
24295     commit : function(){
24296         this.dirty = false;
24297         delete this.modified;
24298         this.editing = false;
24299         if(this.store){
24300             this.store.afterCommit(this);
24301         }
24302     },
24303
24304     // private
24305     hasError : function(){
24306         return this.error != null;
24307     },
24308
24309     // private
24310     clearError : function(){
24311         this.error = null;
24312     },
24313
24314     /**
24315      * Creates a copy of this record.
24316      * @param {String} id (optional) A new record id if you don't want to use this record's id
24317      * @return {Record}
24318      */
24319     copy : function(newId) {
24320         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24321     }
24322 };/*
24323  * Based on:
24324  * Ext JS Library 1.1.1
24325  * Copyright(c) 2006-2007, Ext JS, LLC.
24326  *
24327  * Originally Released Under LGPL - original licence link has changed is not relivant.
24328  *
24329  * Fork - LGPL
24330  * <script type="text/javascript">
24331  */
24332
24333
24334
24335 /**
24336  * @class Roo.data.Store
24337  * @extends Roo.util.Observable
24338  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24339  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24340  * <p>
24341  * 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
24342  * has no knowledge of the format of the data returned by the Proxy.<br>
24343  * <p>
24344  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24345  * instances from the data object. These records are cached and made available through accessor functions.
24346  * @constructor
24347  * Creates a new Store.
24348  * @param {Object} config A config object containing the objects needed for the Store to access data,
24349  * and read the data into Records.
24350  */
24351 Roo.data.Store = function(config){
24352     this.data = new Roo.util.MixedCollection(false);
24353     this.data.getKey = function(o){
24354         return o.id;
24355     };
24356     this.baseParams = {};
24357     // private
24358     this.paramNames = {
24359         "start" : "start",
24360         "limit" : "limit",
24361         "sort" : "sort",
24362         "dir" : "dir",
24363         "multisort" : "_multisort"
24364     };
24365
24366     if(config && config.data){
24367         this.inlineData = config.data;
24368         delete config.data;
24369     }
24370
24371     Roo.apply(this, config);
24372     
24373     if(this.reader){ // reader passed
24374         this.reader = Roo.factory(this.reader, Roo.data);
24375         this.reader.xmodule = this.xmodule || false;
24376         if(!this.recordType){
24377             this.recordType = this.reader.recordType;
24378         }
24379         if(this.reader.onMetaChange){
24380             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24381         }
24382     }
24383
24384     if(this.recordType){
24385         this.fields = this.recordType.prototype.fields;
24386     }
24387     this.modified = [];
24388
24389     this.addEvents({
24390         /**
24391          * @event datachanged
24392          * Fires when the data cache has changed, and a widget which is using this Store
24393          * as a Record cache should refresh its view.
24394          * @param {Store} this
24395          */
24396         datachanged : true,
24397         /**
24398          * @event metachange
24399          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24400          * @param {Store} this
24401          * @param {Object} meta The JSON metadata
24402          */
24403         metachange : true,
24404         /**
24405          * @event add
24406          * Fires when Records have been added to the Store
24407          * @param {Store} this
24408          * @param {Roo.data.Record[]} records The array of Records added
24409          * @param {Number} index The index at which the record(s) were added
24410          */
24411         add : true,
24412         /**
24413          * @event remove
24414          * Fires when a Record has been removed from the Store
24415          * @param {Store} this
24416          * @param {Roo.data.Record} record The Record that was removed
24417          * @param {Number} index The index at which the record was removed
24418          */
24419         remove : true,
24420         /**
24421          * @event update
24422          * Fires when a Record has been updated
24423          * @param {Store} this
24424          * @param {Roo.data.Record} record The Record that was updated
24425          * @param {String} operation The update operation being performed.  Value may be one of:
24426          * <pre><code>
24427  Roo.data.Record.EDIT
24428  Roo.data.Record.REJECT
24429  Roo.data.Record.COMMIT
24430          * </code></pre>
24431          */
24432         update : true,
24433         /**
24434          * @event clear
24435          * Fires when the data cache has been cleared.
24436          * @param {Store} this
24437          */
24438         clear : true,
24439         /**
24440          * @event beforeload
24441          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24442          * the load action will be canceled.
24443          * @param {Store} this
24444          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24445          */
24446         beforeload : true,
24447         /**
24448          * @event beforeloadadd
24449          * Fires after a new set of Records has been loaded.
24450          * @param {Store} this
24451          * @param {Roo.data.Record[]} records The Records that were loaded
24452          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24453          */
24454         beforeloadadd : true,
24455         /**
24456          * @event load
24457          * Fires after a new set of Records has been loaded, before they are added to the store.
24458          * @param {Store} this
24459          * @param {Roo.data.Record[]} records The Records that were loaded
24460          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24461          * @params {Object} return from reader
24462          */
24463         load : true,
24464         /**
24465          * @event loadexception
24466          * Fires if an exception occurs in the Proxy during loading.
24467          * Called with the signature of the Proxy's "loadexception" event.
24468          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24469          * 
24470          * @param {Proxy} 
24471          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24472          * @param {Object} load options 
24473          * @param {Object} jsonData from your request (normally this contains the Exception)
24474          */
24475         loadexception : true
24476     });
24477     
24478     if(this.proxy){
24479         this.proxy = Roo.factory(this.proxy, Roo.data);
24480         this.proxy.xmodule = this.xmodule || false;
24481         this.relayEvents(this.proxy,  ["loadexception"]);
24482     }
24483     this.sortToggle = {};
24484     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24485
24486     Roo.data.Store.superclass.constructor.call(this);
24487
24488     if(this.inlineData){
24489         this.loadData(this.inlineData);
24490         delete this.inlineData;
24491     }
24492 };
24493
24494 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24495      /**
24496     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24497     * without a remote query - used by combo/forms at present.
24498     */
24499     
24500     /**
24501     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24502     */
24503     /**
24504     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24505     */
24506     /**
24507     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24508     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24509     */
24510     /**
24511     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24512     * on any HTTP request
24513     */
24514     /**
24515     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24516     */
24517     /**
24518     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24519     */
24520     multiSort: false,
24521     /**
24522     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24523     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24524     */
24525     remoteSort : false,
24526
24527     /**
24528     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24529      * loaded or when a record is removed. (defaults to false).
24530     */
24531     pruneModifiedRecords : false,
24532
24533     // private
24534     lastOptions : null,
24535
24536     /**
24537      * Add Records to the Store and fires the add event.
24538      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24539      */
24540     add : function(records){
24541         records = [].concat(records);
24542         for(var i = 0, len = records.length; i < len; i++){
24543             records[i].join(this);
24544         }
24545         var index = this.data.length;
24546         this.data.addAll(records);
24547         this.fireEvent("add", this, records, index);
24548     },
24549
24550     /**
24551      * Remove a Record from the Store and fires the remove event.
24552      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24553      */
24554     remove : function(record){
24555         var index = this.data.indexOf(record);
24556         this.data.removeAt(index);
24557  
24558         if(this.pruneModifiedRecords){
24559             this.modified.remove(record);
24560         }
24561         this.fireEvent("remove", this, record, index);
24562     },
24563
24564     /**
24565      * Remove all Records from the Store and fires the clear event.
24566      */
24567     removeAll : function(){
24568         this.data.clear();
24569         if(this.pruneModifiedRecords){
24570             this.modified = [];
24571         }
24572         this.fireEvent("clear", this);
24573     },
24574
24575     /**
24576      * Inserts Records to the Store at the given index and fires the add event.
24577      * @param {Number} index The start index at which to insert the passed Records.
24578      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24579      */
24580     insert : function(index, records){
24581         records = [].concat(records);
24582         for(var i = 0, len = records.length; i < len; i++){
24583             this.data.insert(index, records[i]);
24584             records[i].join(this);
24585         }
24586         this.fireEvent("add", this, records, index);
24587     },
24588
24589     /**
24590      * Get the index within the cache of the passed Record.
24591      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24592      * @return {Number} The index of the passed Record. Returns -1 if not found.
24593      */
24594     indexOf : function(record){
24595         return this.data.indexOf(record);
24596     },
24597
24598     /**
24599      * Get the index within the cache of the Record with the passed id.
24600      * @param {String} id The id of the Record to find.
24601      * @return {Number} The index of the Record. Returns -1 if not found.
24602      */
24603     indexOfId : function(id){
24604         return this.data.indexOfKey(id);
24605     },
24606
24607     /**
24608      * Get the Record with the specified id.
24609      * @param {String} id The id of the Record to find.
24610      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24611      */
24612     getById : function(id){
24613         return this.data.key(id);
24614     },
24615
24616     /**
24617      * Get the Record at the specified index.
24618      * @param {Number} index The index of the Record to find.
24619      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24620      */
24621     getAt : function(index){
24622         return this.data.itemAt(index);
24623     },
24624
24625     /**
24626      * Returns a range of Records between specified indices.
24627      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24628      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24629      * @return {Roo.data.Record[]} An array of Records
24630      */
24631     getRange : function(start, end){
24632         return this.data.getRange(start, end);
24633     },
24634
24635     // private
24636     storeOptions : function(o){
24637         o = Roo.apply({}, o);
24638         delete o.callback;
24639         delete o.scope;
24640         this.lastOptions = o;
24641     },
24642
24643     /**
24644      * Loads the Record cache from the configured Proxy using the configured Reader.
24645      * <p>
24646      * If using remote paging, then the first load call must specify the <em>start</em>
24647      * and <em>limit</em> properties in the options.params property to establish the initial
24648      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24649      * <p>
24650      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24651      * and this call will return before the new data has been loaded. Perform any post-processing
24652      * in a callback function, or in a "load" event handler.</strong>
24653      * <p>
24654      * @param {Object} options An object containing properties which control loading options:<ul>
24655      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24656      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24657      * <pre>
24658                 {
24659                     data : data,  // array of key=>value data like JsonReader
24660                     total : data.length,
24661                     success : true
24662                     
24663                 }
24664         </pre>
24665             }.</li>
24666      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24667      * passed the following arguments:<ul>
24668      * <li>r : Roo.data.Record[]</li>
24669      * <li>options: Options object from the load call</li>
24670      * <li>success: Boolean success indicator</li></ul></li>
24671      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24672      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24673      * </ul>
24674      */
24675     load : function(options){
24676         options = options || {};
24677         if(this.fireEvent("beforeload", this, options) !== false){
24678             this.storeOptions(options);
24679             var p = Roo.apply(options.params || {}, this.baseParams);
24680             // if meta was not loaded from remote source.. try requesting it.
24681             if (!this.reader.metaFromRemote) {
24682                 p._requestMeta = 1;
24683             }
24684             if(this.sortInfo && this.remoteSort){
24685                 var pn = this.paramNames;
24686                 p[pn["sort"]] = this.sortInfo.field;
24687                 p[pn["dir"]] = this.sortInfo.direction;
24688             }
24689             if (this.multiSort) {
24690                 var pn = this.paramNames;
24691                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24692             }
24693             
24694             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24695         }
24696     },
24697
24698     /**
24699      * Reloads the Record cache from the configured Proxy using the configured Reader and
24700      * the options from the last load operation performed.
24701      * @param {Object} options (optional) An object containing properties which may override the options
24702      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24703      * the most recently used options are reused).
24704      */
24705     reload : function(options){
24706         this.load(Roo.applyIf(options||{}, this.lastOptions));
24707     },
24708
24709     // private
24710     // Called as a callback by the Reader during a load operation.
24711     loadRecords : function(o, options, success){
24712          
24713         if(!o){
24714             if(success !== false){
24715                 this.fireEvent("load", this, [], options, o);
24716             }
24717             if(options.callback){
24718                 options.callback.call(options.scope || this, [], options, false);
24719             }
24720             return;
24721         }
24722         // if data returned failure - throw an exception.
24723         if (o.success === false) {
24724             // show a message if no listener is registered.
24725             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24726                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24727             }
24728             // loadmask wil be hooked into this..
24729             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24730             return;
24731         }
24732         var r = o.records, t = o.totalRecords || r.length;
24733         
24734         this.fireEvent("beforeloadadd", this, r, options, o);
24735         
24736         if(!options || options.add !== true){
24737             if(this.pruneModifiedRecords){
24738                 this.modified = [];
24739             }
24740             for(var i = 0, len = r.length; i < len; i++){
24741                 r[i].join(this);
24742             }
24743             if(this.snapshot){
24744                 this.data = this.snapshot;
24745                 delete this.snapshot;
24746             }
24747             this.data.clear();
24748             this.data.addAll(r);
24749             this.totalLength = t;
24750             this.applySort();
24751             this.fireEvent("datachanged", this);
24752         }else{
24753             this.totalLength = Math.max(t, this.data.length+r.length);
24754             this.add(r);
24755         }
24756         
24757         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24758                 
24759             var e = new Roo.data.Record({});
24760
24761             e.set(this.parent.displayField, this.parent.emptyTitle);
24762             e.set(this.parent.valueField, '');
24763
24764             this.insert(0, e);
24765         }
24766             
24767         this.fireEvent("load", this, r, options, o);
24768         if(options.callback){
24769             options.callback.call(options.scope || this, r, options, true);
24770         }
24771     },
24772
24773
24774     /**
24775      * Loads data from a passed data block. A Reader which understands the format of the data
24776      * must have been configured in the constructor.
24777      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24778      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24779      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24780      */
24781     loadData : function(o, append){
24782         var r = this.reader.readRecords(o);
24783         this.loadRecords(r, {add: append}, true);
24784     },
24785     
24786      /**
24787      * using 'cn' the nested child reader read the child array into it's child stores.
24788      * @param {Object} rec The record with a 'children array
24789      */
24790     loadDataFromChildren : function(rec)
24791     {
24792         this.loadData(this.reader.toLoadData(rec));
24793     },
24794     
24795
24796     /**
24797      * Gets the number of cached records.
24798      * <p>
24799      * <em>If using paging, this may not be the total size of the dataset. If the data object
24800      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24801      * the data set size</em>
24802      */
24803     getCount : function(){
24804         return this.data.length || 0;
24805     },
24806
24807     /**
24808      * Gets the total number of records in the dataset as returned by the server.
24809      * <p>
24810      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24811      * the dataset size</em>
24812      */
24813     getTotalCount : function(){
24814         return this.totalLength || 0;
24815     },
24816
24817     /**
24818      * Returns the sort state of the Store as an object with two properties:
24819      * <pre><code>
24820  field {String} The name of the field by which the Records are sorted
24821  direction {String} The sort order, "ASC" or "DESC"
24822      * </code></pre>
24823      */
24824     getSortState : function(){
24825         return this.sortInfo;
24826     },
24827
24828     // private
24829     applySort : function(){
24830         if(this.sortInfo && !this.remoteSort){
24831             var s = this.sortInfo, f = s.field;
24832             var st = this.fields.get(f).sortType;
24833             var fn = function(r1, r2){
24834                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24835                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24836             };
24837             this.data.sort(s.direction, fn);
24838             if(this.snapshot && this.snapshot != this.data){
24839                 this.snapshot.sort(s.direction, fn);
24840             }
24841         }
24842     },
24843
24844     /**
24845      * Sets the default sort column and order to be used by the next load operation.
24846      * @param {String} fieldName The name of the field to sort by.
24847      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24848      */
24849     setDefaultSort : function(field, dir){
24850         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24851     },
24852
24853     /**
24854      * Sort the Records.
24855      * If remote sorting is used, the sort is performed on the server, and the cache is
24856      * reloaded. If local sorting is used, the cache is sorted internally.
24857      * @param {String} fieldName The name of the field to sort by.
24858      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24859      */
24860     sort : function(fieldName, dir){
24861         var f = this.fields.get(fieldName);
24862         if(!dir){
24863             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24864             
24865             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24866                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24867             }else{
24868                 dir = f.sortDir;
24869             }
24870         }
24871         this.sortToggle[f.name] = dir;
24872         this.sortInfo = {field: f.name, direction: dir};
24873         if(!this.remoteSort){
24874             this.applySort();
24875             this.fireEvent("datachanged", this);
24876         }else{
24877             this.load(this.lastOptions);
24878         }
24879     },
24880
24881     /**
24882      * Calls the specified function for each of the Records in the cache.
24883      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24884      * Returning <em>false</em> aborts and exits the iteration.
24885      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24886      */
24887     each : function(fn, scope){
24888         this.data.each(fn, scope);
24889     },
24890
24891     /**
24892      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24893      * (e.g., during paging).
24894      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24895      */
24896     getModifiedRecords : function(){
24897         return this.modified;
24898     },
24899
24900     // private
24901     createFilterFn : function(property, value, anyMatch){
24902         if(!value.exec){ // not a regex
24903             value = String(value);
24904             if(value.length == 0){
24905                 return false;
24906             }
24907             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24908         }
24909         return function(r){
24910             return value.test(r.data[property]);
24911         };
24912     },
24913
24914     /**
24915      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24916      * @param {String} property A field on your records
24917      * @param {Number} start The record index to start at (defaults to 0)
24918      * @param {Number} end The last record index to include (defaults to length - 1)
24919      * @return {Number} The sum
24920      */
24921     sum : function(property, start, end){
24922         var rs = this.data.items, v = 0;
24923         start = start || 0;
24924         end = (end || end === 0) ? end : rs.length-1;
24925
24926         for(var i = start; i <= end; i++){
24927             v += (rs[i].data[property] || 0);
24928         }
24929         return v;
24930     },
24931
24932     /**
24933      * Filter the records by a specified property.
24934      * @param {String} field A field on your records
24935      * @param {String/RegExp} value Either a string that the field
24936      * should start with or a RegExp to test against the field
24937      * @param {Boolean} anyMatch True to match any part not just the beginning
24938      */
24939     filter : function(property, value, anyMatch){
24940         var fn = this.createFilterFn(property, value, anyMatch);
24941         return fn ? this.filterBy(fn) : this.clearFilter();
24942     },
24943
24944     /**
24945      * Filter by a function. The specified function will be called with each
24946      * record in this data source. If the function returns true the record is included,
24947      * otherwise it is filtered.
24948      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24949      * @param {Object} scope (optional) The scope of the function (defaults to this)
24950      */
24951     filterBy : function(fn, scope){
24952         this.snapshot = this.snapshot || this.data;
24953         this.data = this.queryBy(fn, scope||this);
24954         this.fireEvent("datachanged", this);
24955     },
24956
24957     /**
24958      * Query the records by a specified property.
24959      * @param {String} field A field on your records
24960      * @param {String/RegExp} value Either a string that the field
24961      * should start with or a RegExp to test against the field
24962      * @param {Boolean} anyMatch True to match any part not just the beginning
24963      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24964      */
24965     query : function(property, value, anyMatch){
24966         var fn = this.createFilterFn(property, value, anyMatch);
24967         return fn ? this.queryBy(fn) : this.data.clone();
24968     },
24969
24970     /**
24971      * Query by a function. The specified function will be called with each
24972      * record in this data source. If the function returns true the record is included
24973      * in the results.
24974      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24975      * @param {Object} scope (optional) The scope of the function (defaults to this)
24976       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24977      **/
24978     queryBy : function(fn, scope){
24979         var data = this.snapshot || this.data;
24980         return data.filterBy(fn, scope||this);
24981     },
24982
24983     /**
24984      * Collects unique values for a particular dataIndex from this store.
24985      * @param {String} dataIndex The property to collect
24986      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24987      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24988      * @return {Array} An array of the unique values
24989      **/
24990     collect : function(dataIndex, allowNull, bypassFilter){
24991         var d = (bypassFilter === true && this.snapshot) ?
24992                 this.snapshot.items : this.data.items;
24993         var v, sv, r = [], l = {};
24994         for(var i = 0, len = d.length; i < len; i++){
24995             v = d[i].data[dataIndex];
24996             sv = String(v);
24997             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24998                 l[sv] = true;
24999                 r[r.length] = v;
25000             }
25001         }
25002         return r;
25003     },
25004
25005     /**
25006      * Revert to a view of the Record cache with no filtering applied.
25007      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25008      */
25009     clearFilter : function(suppressEvent){
25010         if(this.snapshot && this.snapshot != this.data){
25011             this.data = this.snapshot;
25012             delete this.snapshot;
25013             if(suppressEvent !== true){
25014                 this.fireEvent("datachanged", this);
25015             }
25016         }
25017     },
25018
25019     // private
25020     afterEdit : function(record){
25021         if(this.modified.indexOf(record) == -1){
25022             this.modified.push(record);
25023         }
25024         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25025     },
25026     
25027     // private
25028     afterReject : function(record){
25029         this.modified.remove(record);
25030         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25031     },
25032
25033     // private
25034     afterCommit : function(record){
25035         this.modified.remove(record);
25036         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25037     },
25038
25039     /**
25040      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25041      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25042      */
25043     commitChanges : function(){
25044         var m = this.modified.slice(0);
25045         this.modified = [];
25046         for(var i = 0, len = m.length; i < len; i++){
25047             m[i].commit();
25048         }
25049     },
25050
25051     /**
25052      * Cancel outstanding changes on all changed records.
25053      */
25054     rejectChanges : function(){
25055         var m = this.modified.slice(0);
25056         this.modified = [];
25057         for(var i = 0, len = m.length; i < len; i++){
25058             m[i].reject();
25059         }
25060     },
25061
25062     onMetaChange : function(meta, rtype, o){
25063         this.recordType = rtype;
25064         this.fields = rtype.prototype.fields;
25065         delete this.snapshot;
25066         this.sortInfo = meta.sortInfo || this.sortInfo;
25067         this.modified = [];
25068         this.fireEvent('metachange', this, this.reader.meta);
25069     },
25070     
25071     moveIndex : function(data, type)
25072     {
25073         var index = this.indexOf(data);
25074         
25075         var newIndex = index + type;
25076         
25077         this.remove(data);
25078         
25079         this.insert(newIndex, data);
25080         
25081     }
25082 });/*
25083  * Based on:
25084  * Ext JS Library 1.1.1
25085  * Copyright(c) 2006-2007, Ext JS, LLC.
25086  *
25087  * Originally Released Under LGPL - original licence link has changed is not relivant.
25088  *
25089  * Fork - LGPL
25090  * <script type="text/javascript">
25091  */
25092
25093 /**
25094  * @class Roo.data.SimpleStore
25095  * @extends Roo.data.Store
25096  * Small helper class to make creating Stores from Array data easier.
25097  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25098  * @cfg {Array} fields An array of field definition objects, or field name strings.
25099  * @cfg {Object} an existing reader (eg. copied from another store)
25100  * @cfg {Array} data The multi-dimensional array of data
25101  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25102  * @cfg {Roo.data.Reader} reader  [not-required] 
25103  * @constructor
25104  * @param {Object} config
25105  */
25106 Roo.data.SimpleStore = function(config)
25107 {
25108     Roo.data.SimpleStore.superclass.constructor.call(this, {
25109         isLocal : true,
25110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25111                 id: config.id
25112             },
25113             Roo.data.Record.create(config.fields)
25114         ),
25115         proxy : new Roo.data.MemoryProxy(config.data)
25116     });
25117     this.load();
25118 };
25119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25120  * Based on:
25121  * Ext JS Library 1.1.1
25122  * Copyright(c) 2006-2007, Ext JS, LLC.
25123  *
25124  * Originally Released Under LGPL - original licence link has changed is not relivant.
25125  *
25126  * Fork - LGPL
25127  * <script type="text/javascript">
25128  */
25129
25130 /**
25131 /**
25132  * @extends Roo.data.Store
25133  * @class Roo.data.JsonStore
25134  * Small helper class to make creating Stores for JSON data easier. <br/>
25135 <pre><code>
25136 var store = new Roo.data.JsonStore({
25137     url: 'get-images.php',
25138     root: 'images',
25139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25140 });
25141 </code></pre>
25142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25143  * JsonReader and HttpProxy (unless inline data is provided).</b>
25144  * @cfg {Array} fields An array of field definition objects, or field name strings.
25145  * @constructor
25146  * @param {Object} config
25147  */
25148 Roo.data.JsonStore = function(c){
25149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25151         reader: new Roo.data.JsonReader(c, c.fields)
25152     }));
25153 };
25154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25155  * Based on:
25156  * Ext JS Library 1.1.1
25157  * Copyright(c) 2006-2007, Ext JS, LLC.
25158  *
25159  * Originally Released Under LGPL - original licence link has changed is not relivant.
25160  *
25161  * Fork - LGPL
25162  * <script type="text/javascript">
25163  */
25164
25165  
25166 Roo.data.Field = function(config){
25167     if(typeof config == "string"){
25168         config = {name: config};
25169     }
25170     Roo.apply(this, config);
25171     
25172     if(!this.type){
25173         this.type = "auto";
25174     }
25175     
25176     var st = Roo.data.SortTypes;
25177     // named sortTypes are supported, here we look them up
25178     if(typeof this.sortType == "string"){
25179         this.sortType = st[this.sortType];
25180     }
25181     
25182     // set default sortType for strings and dates
25183     if(!this.sortType){
25184         switch(this.type){
25185             case "string":
25186                 this.sortType = st.asUCString;
25187                 break;
25188             case "date":
25189                 this.sortType = st.asDate;
25190                 break;
25191             default:
25192                 this.sortType = st.none;
25193         }
25194     }
25195
25196     // define once
25197     var stripRe = /[\$,%]/g;
25198
25199     // prebuilt conversion function for this field, instead of
25200     // switching every time we're reading a value
25201     if(!this.convert){
25202         var cv, dateFormat = this.dateFormat;
25203         switch(this.type){
25204             case "":
25205             case "auto":
25206             case undefined:
25207                 cv = function(v){ return v; };
25208                 break;
25209             case "string":
25210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25211                 break;
25212             case "int":
25213                 cv = function(v){
25214                     return v !== undefined && v !== null && v !== '' ?
25215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25216                     };
25217                 break;
25218             case "float":
25219                 cv = function(v){
25220                     return v !== undefined && v !== null && v !== '' ?
25221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25222                     };
25223                 break;
25224             case "bool":
25225             case "boolean":
25226                 cv = function(v){ return v === true || v === "true" || v == 1; };
25227                 break;
25228             case "date":
25229                 cv = function(v){
25230                     if(!v){
25231                         return '';
25232                     }
25233                     if(v instanceof Date){
25234                         return v;
25235                     }
25236                     if(dateFormat){
25237                         if(dateFormat == "timestamp"){
25238                             return new Date(v*1000);
25239                         }
25240                         return Date.parseDate(v, dateFormat);
25241                     }
25242                     var parsed = Date.parse(v);
25243                     return parsed ? new Date(parsed) : null;
25244                 };
25245              break;
25246             
25247         }
25248         this.convert = cv;
25249     }
25250 };
25251
25252 Roo.data.Field.prototype = {
25253     dateFormat: null,
25254     defaultValue: "",
25255     mapping: null,
25256     sortType : null,
25257     sortDir : "ASC"
25258 };/*
25259  * Based on:
25260  * Ext JS Library 1.1.1
25261  * Copyright(c) 2006-2007, Ext JS, LLC.
25262  *
25263  * Originally Released Under LGPL - original licence link has changed is not relivant.
25264  *
25265  * Fork - LGPL
25266  * <script type="text/javascript">
25267  */
25268  
25269 // Base class for reading structured data from a data source.  This class is intended to be
25270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25271
25272 /**
25273  * @class Roo.data.DataReader
25274  * @abstract
25275  * Base class for reading structured data from a data source.  This class is intended to be
25276  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25277  */
25278
25279 Roo.data.DataReader = function(meta, recordType){
25280     
25281     this.meta = meta;
25282     
25283     this.recordType = recordType instanceof Array ? 
25284         Roo.data.Record.create(recordType) : recordType;
25285 };
25286
25287 Roo.data.DataReader.prototype = {
25288     
25289     
25290     readerType : 'Data',
25291      /**
25292      * Create an empty record
25293      * @param {Object} data (optional) - overlay some values
25294      * @return {Roo.data.Record} record created.
25295      */
25296     newRow :  function(d) {
25297         var da =  {};
25298         this.recordType.prototype.fields.each(function(c) {
25299             switch( c.type) {
25300                 case 'int' : da[c.name] = 0; break;
25301                 case 'date' : da[c.name] = new Date(); break;
25302                 case 'float' : da[c.name] = 0.0; break;
25303                 case 'boolean' : da[c.name] = false; break;
25304                 default : da[c.name] = ""; break;
25305             }
25306             
25307         });
25308         return new this.recordType(Roo.apply(da, d));
25309     }
25310     
25311     
25312 };/*
25313  * Based on:
25314  * Ext JS Library 1.1.1
25315  * Copyright(c) 2006-2007, Ext JS, LLC.
25316  *
25317  * Originally Released Under LGPL - original licence link has changed is not relivant.
25318  *
25319  * Fork - LGPL
25320  * <script type="text/javascript">
25321  */
25322
25323 /**
25324  * @class Roo.data.DataProxy
25325  * @extends Roo.util.Observable
25326  * @abstract
25327  * This class is an abstract base class for implementations which provide retrieval of
25328  * unformatted data objects.<br>
25329  * <p>
25330  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25331  * (of the appropriate type which knows how to parse the data object) to provide a block of
25332  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25333  * <p>
25334  * Custom implementations must implement the load method as described in
25335  * {@link Roo.data.HttpProxy#load}.
25336  */
25337 Roo.data.DataProxy = function(){
25338     this.addEvents({
25339         /**
25340          * @event beforeload
25341          * Fires before a network request is made to retrieve a data object.
25342          * @param {Object} This DataProxy object.
25343          * @param {Object} params The params parameter to the load function.
25344          */
25345         beforeload : true,
25346         /**
25347          * @event load
25348          * Fires before the load method's callback is called.
25349          * @param {Object} This DataProxy object.
25350          * @param {Object} o The data object.
25351          * @param {Object} arg The callback argument object passed to the load function.
25352          */
25353         load : true,
25354         /**
25355          * @event loadexception
25356          * Fires if an Exception occurs during data retrieval.
25357          * @param {Object} This DataProxy object.
25358          * @param {Object} o The data object.
25359          * @param {Object} arg The callback argument object passed to the load function.
25360          * @param {Object} e The Exception.
25361          */
25362         loadexception : true
25363     });
25364     Roo.data.DataProxy.superclass.constructor.call(this);
25365 };
25366
25367 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25368
25369     /**
25370      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25371      */
25372 /*
25373  * Based on:
25374  * Ext JS Library 1.1.1
25375  * Copyright(c) 2006-2007, Ext JS, LLC.
25376  *
25377  * Originally Released Under LGPL - original licence link has changed is not relivant.
25378  *
25379  * Fork - LGPL
25380  * <script type="text/javascript">
25381  */
25382 /**
25383  * @class Roo.data.MemoryProxy
25384  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25385  * to the Reader when its load method is called.
25386  * @constructor
25387  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25388  */
25389 Roo.data.MemoryProxy = function(data){
25390     if (data.data) {
25391         data = data.data;
25392     }
25393     Roo.data.MemoryProxy.superclass.constructor.call(this);
25394     this.data = data;
25395 };
25396
25397 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25398     
25399     /**
25400      * Load data from the requested source (in this case an in-memory
25401      * data object passed to the constructor), read the data object into
25402      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25403      * process that block using the passed callback.
25404      * @param {Object} params This parameter is not used by the MemoryProxy class.
25405      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25406      * object into a block of Roo.data.Records.
25407      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25408      * The function must be passed <ul>
25409      * <li>The Record block object</li>
25410      * <li>The "arg" argument from the load function</li>
25411      * <li>A boolean success indicator</li>
25412      * </ul>
25413      * @param {Object} scope The scope in which to call the callback
25414      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25415      */
25416     load : function(params, reader, callback, scope, arg){
25417         params = params || {};
25418         var result;
25419         try {
25420             result = reader.readRecords(params.data ? params.data :this.data);
25421         }catch(e){
25422             this.fireEvent("loadexception", this, arg, null, e);
25423             callback.call(scope, null, arg, false);
25424             return;
25425         }
25426         callback.call(scope, result, arg, true);
25427     },
25428     
25429     // private
25430     update : function(params, records){
25431         
25432     }
25433 });/*
25434  * Based on:
25435  * Ext JS Library 1.1.1
25436  * Copyright(c) 2006-2007, Ext JS, LLC.
25437  *
25438  * Originally Released Under LGPL - original licence link has changed is not relivant.
25439  *
25440  * Fork - LGPL
25441  * <script type="text/javascript">
25442  */
25443 /**
25444  * @class Roo.data.HttpProxy
25445  * @extends Roo.data.DataProxy
25446  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25447  * configured to reference a certain URL.<br><br>
25448  * <p>
25449  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25450  * from which the running page was served.<br><br>
25451  * <p>
25452  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25453  * <p>
25454  * Be aware that to enable the browser to parse an XML document, the server must set
25455  * the Content-Type header in the HTTP response to "text/xml".
25456  * @constructor
25457  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25458  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25459  * will be used to make the request.
25460  */
25461 Roo.data.HttpProxy = function(conn){
25462     Roo.data.HttpProxy.superclass.constructor.call(this);
25463     // is conn a conn config or a real conn?
25464     this.conn = conn;
25465     this.useAjax = !conn || !conn.events;
25466   
25467 };
25468
25469 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25470     // thse are take from connection...
25471     
25472     /**
25473      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25474      */
25475     /**
25476      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25477      * extra parameters to each request made by this object. (defaults to undefined)
25478      */
25479     /**
25480      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25481      *  to each request made by this object. (defaults to undefined)
25482      */
25483     /**
25484      * @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)
25485      */
25486     /**
25487      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25488      */
25489      /**
25490      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25491      * @type Boolean
25492      */
25493   
25494
25495     /**
25496      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25497      * @type Boolean
25498      */
25499     /**
25500      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25501      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25502      * a finer-grained basis than the DataProxy events.
25503      */
25504     getConnection : function(){
25505         return this.useAjax ? Roo.Ajax : this.conn;
25506     },
25507
25508     /**
25509      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25510      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25511      * process that block using the passed callback.
25512      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25513      * for the request to the remote server.
25514      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25515      * object into a block of Roo.data.Records.
25516      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25517      * The function must be passed <ul>
25518      * <li>The Record block object</li>
25519      * <li>The "arg" argument from the load function</li>
25520      * <li>A boolean success indicator</li>
25521      * </ul>
25522      * @param {Object} scope The scope in which to call the callback
25523      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25524      */
25525     load : function(params, reader, callback, scope, arg){
25526         if(this.fireEvent("beforeload", this, params) !== false){
25527             var  o = {
25528                 params : params || {},
25529                 request: {
25530                     callback : callback,
25531                     scope : scope,
25532                     arg : arg
25533                 },
25534                 reader: reader,
25535                 callback : this.loadResponse,
25536                 scope: this
25537             };
25538             if(this.useAjax){
25539                 Roo.applyIf(o, this.conn);
25540                 if(this.activeRequest){
25541                     Roo.Ajax.abort(this.activeRequest);
25542                 }
25543                 this.activeRequest = Roo.Ajax.request(o);
25544             }else{
25545                 this.conn.request(o);
25546             }
25547         }else{
25548             callback.call(scope||this, null, arg, false);
25549         }
25550     },
25551
25552     // private
25553     loadResponse : function(o, success, response){
25554         delete this.activeRequest;
25555         if(!success){
25556             this.fireEvent("loadexception", this, o, response);
25557             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25558             return;
25559         }
25560         var result;
25561         try {
25562             result = o.reader.read(response);
25563         }catch(e){
25564             o.success = false;
25565             o.raw = { errorMsg : response.responseText };
25566             this.fireEvent("loadexception", this, o, response, e);
25567             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25568             return;
25569         }
25570         
25571         this.fireEvent("load", this, o, o.request.arg);
25572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25573     },
25574
25575     // private
25576     update : function(dataSet){
25577
25578     },
25579
25580     // private
25581     updateResponse : function(dataSet){
25582
25583     }
25584 });/*
25585  * Based on:
25586  * Ext JS Library 1.1.1
25587  * Copyright(c) 2006-2007, Ext JS, LLC.
25588  *
25589  * Originally Released Under LGPL - original licence link has changed is not relivant.
25590  *
25591  * Fork - LGPL
25592  * <script type="text/javascript">
25593  */
25594
25595 /**
25596  * @class Roo.data.ScriptTagProxy
25597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25598  * other than the originating domain of the running page.<br><br>
25599  * <p>
25600  * <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
25601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25602  * <p>
25603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25604  * source code that is used as the source inside a &lt;script> tag.<br><br>
25605  * <p>
25606  * In order for the browser to process the returned data, the server must wrap the data object
25607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25609  * depending on whether the callback name was passed:
25610  * <p>
25611  * <pre><code>
25612 boolean scriptTag = false;
25613 String cb = request.getParameter("callback");
25614 if (cb != null) {
25615     scriptTag = true;
25616     response.setContentType("text/javascript");
25617 } else {
25618     response.setContentType("application/x-json");
25619 }
25620 Writer out = response.getWriter();
25621 if (scriptTag) {
25622     out.write(cb + "(");
25623 }
25624 out.print(dataBlock.toJsonString());
25625 if (scriptTag) {
25626     out.write(");");
25627 }
25628 </pre></code>
25629  *
25630  * @constructor
25631  * @param {Object} config A configuration object.
25632  */
25633 Roo.data.ScriptTagProxy = function(config){
25634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25635     Roo.apply(this, config);
25636     this.head = document.getElementsByTagName("head")[0];
25637 };
25638
25639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25640
25641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25642     /**
25643      * @cfg {String} url The URL from which to request the data object.
25644      */
25645     /**
25646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25647      */
25648     timeout : 30000,
25649     /**
25650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25651      * the server the name of the callback function set up by the load call to process the returned data object.
25652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25653      * javascript output which calls this named function passing the data object as its only parameter.
25654      */
25655     callbackParam : "callback",
25656     /**
25657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25658      * name to the request.
25659      */
25660     nocache : true,
25661
25662     /**
25663      * Load data from the configured URL, read the data object into
25664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25665      * process that block using the passed callback.
25666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25667      * for the request to the remote server.
25668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25669      * object into a block of Roo.data.Records.
25670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25671      * The function must be passed <ul>
25672      * <li>The Record block object</li>
25673      * <li>The "arg" argument from the load function</li>
25674      * <li>A boolean success indicator</li>
25675      * </ul>
25676      * @param {Object} scope The scope in which to call the callback
25677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25678      */
25679     load : function(params, reader, callback, scope, arg){
25680         if(this.fireEvent("beforeload", this, params) !== false){
25681
25682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25683
25684             var url = this.url;
25685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25686             if(this.nocache){
25687                 url += "&_dc=" + (new Date().getTime());
25688             }
25689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25690             var trans = {
25691                 id : transId,
25692                 cb : "stcCallback"+transId,
25693                 scriptId : "stcScript"+transId,
25694                 params : params,
25695                 arg : arg,
25696                 url : url,
25697                 callback : callback,
25698                 scope : scope,
25699                 reader : reader
25700             };
25701             var conn = this;
25702
25703             window[trans.cb] = function(o){
25704                 conn.handleResponse(o, trans);
25705             };
25706
25707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25708
25709             if(this.autoAbort !== false){
25710                 this.abort();
25711             }
25712
25713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25714
25715             var script = document.createElement("script");
25716             script.setAttribute("src", url);
25717             script.setAttribute("type", "text/javascript");
25718             script.setAttribute("id", trans.scriptId);
25719             this.head.appendChild(script);
25720
25721             this.trans = trans;
25722         }else{
25723             callback.call(scope||this, null, arg, false);
25724         }
25725     },
25726
25727     // private
25728     isLoading : function(){
25729         return this.trans ? true : false;
25730     },
25731
25732     /**
25733      * Abort the current server request.
25734      */
25735     abort : function(){
25736         if(this.isLoading()){
25737             this.destroyTrans(this.trans);
25738         }
25739     },
25740
25741     // private
25742     destroyTrans : function(trans, isLoaded){
25743         this.head.removeChild(document.getElementById(trans.scriptId));
25744         clearTimeout(trans.timeoutId);
25745         if(isLoaded){
25746             window[trans.cb] = undefined;
25747             try{
25748                 delete window[trans.cb];
25749             }catch(e){}
25750         }else{
25751             // if hasn't been loaded, wait for load to remove it to prevent script error
25752             window[trans.cb] = function(){
25753                 window[trans.cb] = undefined;
25754                 try{
25755                     delete window[trans.cb];
25756                 }catch(e){}
25757             };
25758         }
25759     },
25760
25761     // private
25762     handleResponse : function(o, trans){
25763         this.trans = false;
25764         this.destroyTrans(trans, true);
25765         var result;
25766         try {
25767             result = trans.reader.readRecords(o);
25768         }catch(e){
25769             this.fireEvent("loadexception", this, o, trans.arg, e);
25770             trans.callback.call(trans.scope||window, null, trans.arg, false);
25771             return;
25772         }
25773         this.fireEvent("load", this, o, trans.arg);
25774         trans.callback.call(trans.scope||window, result, trans.arg, true);
25775     },
25776
25777     // private
25778     handleFailure : function(trans){
25779         this.trans = false;
25780         this.destroyTrans(trans, false);
25781         this.fireEvent("loadexception", this, null, trans.arg);
25782         trans.callback.call(trans.scope||window, null, trans.arg, false);
25783     }
25784 });/*
25785  * Based on:
25786  * Ext JS Library 1.1.1
25787  * Copyright(c) 2006-2007, Ext JS, LLC.
25788  *
25789  * Originally Released Under LGPL - original licence link has changed is not relivant.
25790  *
25791  * Fork - LGPL
25792  * <script type="text/javascript">
25793  */
25794
25795 /**
25796  * @class Roo.data.JsonReader
25797  * @extends Roo.data.DataReader
25798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25799  * based on mappings in a provided Roo.data.Record constructor.
25800  * 
25801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25802  * in the reply previously. 
25803  * 
25804  * <p>
25805  * Example code:
25806  * <pre><code>
25807 var RecordDef = Roo.data.Record.create([
25808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25810 ]);
25811 var myReader = new Roo.data.JsonReader({
25812     totalProperty: "results",    // The property which contains the total dataset size (optional)
25813     root: "rows",                // The property which contains an Array of row objects
25814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25815 }, RecordDef);
25816 </code></pre>
25817  * <p>
25818  * This would consume a JSON file like this:
25819  * <pre><code>
25820 { 'results': 2, 'rows': [
25821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25823 }
25824 </code></pre>
25825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25827  * paged from the remote server.
25828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25829  * @cfg {String} root name of the property which contains the Array of row objects.
25830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25831  * @cfg {Array} fields Array of field definition objects
25832  * @constructor
25833  * Create a new JsonReader
25834  * @param {Object} meta Metadata configuration options
25835  * @param {Object} recordType Either an Array of field definition objects,
25836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25837  */
25838 Roo.data.JsonReader = function(meta, recordType){
25839     
25840     meta = meta || {};
25841     // set some defaults:
25842     Roo.applyIf(meta, {
25843         totalProperty: 'total',
25844         successProperty : 'success',
25845         root : 'data',
25846         id : 'id'
25847     });
25848     
25849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25850 };
25851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25852     
25853     readerType : 'Json',
25854     
25855     /**
25856      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25857      * Used by Store query builder to append _requestMeta to params.
25858      * 
25859      */
25860     metaFromRemote : false,
25861     /**
25862      * This method is only used by a DataProxy which has retrieved data from a remote server.
25863      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25864      * @return {Object} data A data block which is used by an Roo.data.Store object as
25865      * a cache of Roo.data.Records.
25866      */
25867     read : function(response){
25868         var json = response.responseText;
25869        
25870         var o = /* eval:var:o */ eval("("+json+")");
25871         if(!o) {
25872             throw {message: "JsonReader.read: Json object not found"};
25873         }
25874         
25875         if(o.metaData){
25876             
25877             delete this.ef;
25878             this.metaFromRemote = true;
25879             this.meta = o.metaData;
25880             this.recordType = Roo.data.Record.create(o.metaData.fields);
25881             this.onMetaChange(this.meta, this.recordType, o);
25882         }
25883         return this.readRecords(o);
25884     },
25885
25886     // private function a store will implement
25887     onMetaChange : function(meta, recordType, o){
25888
25889     },
25890
25891     /**
25892          * @ignore
25893          */
25894     simpleAccess: function(obj, subsc) {
25895         return obj[subsc];
25896     },
25897
25898         /**
25899          * @ignore
25900          */
25901     getJsonAccessor: function(){
25902         var re = /[\[\.]/;
25903         return function(expr) {
25904             try {
25905                 return(re.test(expr))
25906                     ? new Function("obj", "return obj." + expr)
25907                     : function(obj){
25908                         return obj[expr];
25909                     };
25910             } catch(e){}
25911             return Roo.emptyFn;
25912         };
25913     }(),
25914
25915     /**
25916      * Create a data block containing Roo.data.Records from an XML document.
25917      * @param {Object} o An object which contains an Array of row objects in the property specified
25918      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25919      * which contains the total size of the dataset.
25920      * @return {Object} data A data block which is used by an Roo.data.Store object as
25921      * a cache of Roo.data.Records.
25922      */
25923     readRecords : function(o){
25924         /**
25925          * After any data loads, the raw JSON data is available for further custom processing.
25926          * @type Object
25927          */
25928         this.o = o;
25929         var s = this.meta, Record = this.recordType,
25930             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25931
25932 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25933         if (!this.ef) {
25934             if(s.totalProperty) {
25935                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25936                 }
25937                 if(s.successProperty) {
25938                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25939                 }
25940                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25941                 if (s.id) {
25942                         var g = this.getJsonAccessor(s.id);
25943                         this.getId = function(rec) {
25944                                 var r = g(rec);  
25945                                 return (r === undefined || r === "") ? null : r;
25946                         };
25947                 } else {
25948                         this.getId = function(){return null;};
25949                 }
25950             this.ef = [];
25951             for(var jj = 0; jj < fl; jj++){
25952                 f = fi[jj];
25953                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25954                 this.ef[jj] = this.getJsonAccessor(map);
25955             }
25956         }
25957
25958         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25959         if(s.totalProperty){
25960             var vt = parseInt(this.getTotal(o), 10);
25961             if(!isNaN(vt)){
25962                 totalRecords = vt;
25963             }
25964         }
25965         if(s.successProperty){
25966             var vs = this.getSuccess(o);
25967             if(vs === false || vs === 'false'){
25968                 success = false;
25969             }
25970         }
25971         var records = [];
25972         for(var i = 0; i < c; i++){
25973             var n = root[i];
25974             var values = {};
25975             var id = this.getId(n);
25976             for(var j = 0; j < fl; j++){
25977                 f = fi[j];
25978                                 var v = this.ef[j](n);
25979                                 if (!f.convert) {
25980                                         Roo.log('missing convert for ' + f.name);
25981                                         Roo.log(f);
25982                                         continue;
25983                                 }
25984                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25985             }
25986                         if (!Record) {
25987                                 return {
25988                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25989                                         success : false,
25990                                         records : [],
25991                                         totalRecords : 0
25992                                 };
25993                         }
25994             var record = new Record(values, id);
25995             record.json = n;
25996             records[i] = record;
25997         }
25998         return {
25999             raw : o,
26000             success : success,
26001             records : records,
26002             totalRecords : totalRecords
26003         };
26004     },
26005     // used when loading children.. @see loadDataFromChildren
26006     toLoadData: function(rec)
26007     {
26008         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26009         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26010         return { data : data, total : data.length };
26011         
26012     }
26013 });/*
26014  * Based on:
26015  * Ext JS Library 1.1.1
26016  * Copyright(c) 2006-2007, Ext JS, LLC.
26017  *
26018  * Originally Released Under LGPL - original licence link has changed is not relivant.
26019  *
26020  * Fork - LGPL
26021  * <script type="text/javascript">
26022  */
26023
26024 /**
26025  * @class Roo.data.XmlReader
26026  * @extends Roo.data.DataReader
26027  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26028  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26029  * <p>
26030  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26031  * header in the HTTP response must be set to "text/xml".</em>
26032  * <p>
26033  * Example code:
26034  * <pre><code>
26035 var RecordDef = Roo.data.Record.create([
26036    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26037    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26038 ]);
26039 var myReader = new Roo.data.XmlReader({
26040    totalRecords: "results", // The element which contains the total dataset size (optional)
26041    record: "row",           // The repeated element which contains row information
26042    id: "id"                 // The element within the row that provides an ID for the record (optional)
26043 }, RecordDef);
26044 </code></pre>
26045  * <p>
26046  * This would consume an XML file like this:
26047  * <pre><code>
26048 &lt;?xml?>
26049 &lt;dataset>
26050  &lt;results>2&lt;/results>
26051  &lt;row>
26052    &lt;id>1&lt;/id>
26053    &lt;name>Bill&lt;/name>
26054    &lt;occupation>Gardener&lt;/occupation>
26055  &lt;/row>
26056  &lt;row>
26057    &lt;id>2&lt;/id>
26058    &lt;name>Ben&lt;/name>
26059    &lt;occupation>Horticulturalist&lt;/occupation>
26060  &lt;/row>
26061 &lt;/dataset>
26062 </code></pre>
26063  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26064  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26065  * paged from the remote server.
26066  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26067  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26068  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26069  * a record identifier value.
26070  * @constructor
26071  * Create a new XmlReader
26072  * @param {Object} meta Metadata configuration options
26073  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26074  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26075  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26076  */
26077 Roo.data.XmlReader = function(meta, recordType){
26078     meta = meta || {};
26079     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26080 };
26081 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26082     
26083     readerType : 'Xml',
26084     
26085     /**
26086      * This method is only used by a DataProxy which has retrieved data from a remote server.
26087          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26088          * to contain a method called 'responseXML' that returns an XML document object.
26089      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26090      * a cache of Roo.data.Records.
26091      */
26092     read : function(response){
26093         var doc = response.responseXML;
26094         if(!doc) {
26095             throw {message: "XmlReader.read: XML Document not available"};
26096         }
26097         return this.readRecords(doc);
26098     },
26099
26100     /**
26101      * Create a data block containing Roo.data.Records from an XML document.
26102          * @param {Object} doc A parsed XML document.
26103      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26104      * a cache of Roo.data.Records.
26105      */
26106     readRecords : function(doc){
26107         /**
26108          * After any data loads/reads, the raw XML Document is available for further custom processing.
26109          * @type XMLDocument
26110          */
26111         this.xmlData = doc;
26112         var root = doc.documentElement || doc;
26113         var q = Roo.DomQuery;
26114         var recordType = this.recordType, fields = recordType.prototype.fields;
26115         var sid = this.meta.id;
26116         var totalRecords = 0, success = true;
26117         if(this.meta.totalRecords){
26118             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26119         }
26120         
26121         if(this.meta.success){
26122             var sv = q.selectValue(this.meta.success, root, true);
26123             success = sv !== false && sv !== 'false';
26124         }
26125         var records = [];
26126         var ns = q.select(this.meta.record, root);
26127         for(var i = 0, len = ns.length; i < len; i++) {
26128                 var n = ns[i];
26129                 var values = {};
26130                 var id = sid ? q.selectValue(sid, n) : undefined;
26131                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26132                     var f = fields.items[j];
26133                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26134                     v = f.convert(v);
26135                     values[f.name] = v;
26136                 }
26137                 var record = new recordType(values, id);
26138                 record.node = n;
26139                 records[records.length] = record;
26140             }
26141
26142             return {
26143                 success : success,
26144                 records : records,
26145                 totalRecords : totalRecords || records.length
26146             };
26147     }
26148 });/*
26149  * Based on:
26150  * Ext JS Library 1.1.1
26151  * Copyright(c) 2006-2007, Ext JS, LLC.
26152  *
26153  * Originally Released Under LGPL - original licence link has changed is not relivant.
26154  *
26155  * Fork - LGPL
26156  * <script type="text/javascript">
26157  */
26158
26159 /**
26160  * @class Roo.data.ArrayReader
26161  * @extends Roo.data.DataReader
26162  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26163  * Each element of that Array represents a row of data fields. The
26164  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26165  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26166  * <p>
26167  * Example code:.
26168  * <pre><code>
26169 var RecordDef = Roo.data.Record.create([
26170     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26171     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26172 ]);
26173 var myReader = new Roo.data.ArrayReader({
26174     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26175 }, RecordDef);
26176 </code></pre>
26177  * <p>
26178  * This would consume an Array like this:
26179  * <pre><code>
26180 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26181   </code></pre>
26182  
26183  * @constructor
26184  * Create a new JsonReader
26185  * @param {Object} meta Metadata configuration options.
26186  * @param {Object|Array} recordType Either an Array of field definition objects
26187  * 
26188  * @cfg {Array} fields Array of field definition objects
26189  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26190  * as specified to {@link Roo.data.Record#create},
26191  * or an {@link Roo.data.Record} object
26192  *
26193  * 
26194  * created using {@link Roo.data.Record#create}.
26195  */
26196 Roo.data.ArrayReader = function(meta, recordType)
26197 {    
26198     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26199 };
26200
26201 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26202     
26203       /**
26204      * Create a data block containing Roo.data.Records from an XML document.
26205      * @param {Object} o An Array of row objects which represents the dataset.
26206      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26207      * a cache of Roo.data.Records.
26208      */
26209     readRecords : function(o)
26210     {
26211         var sid = this.meta ? this.meta.id : null;
26212         var recordType = this.recordType, fields = recordType.prototype.fields;
26213         var records = [];
26214         var root = o;
26215         for(var i = 0; i < root.length; i++){
26216             var n = root[i];
26217             var values = {};
26218             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26219             for(var j = 0, jlen = fields.length; j < jlen; j++){
26220                 var f = fields.items[j];
26221                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26222                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26223                 v = f.convert(v);
26224                 values[f.name] = v;
26225             }
26226             var record = new recordType(values, id);
26227             record.json = n;
26228             records[records.length] = record;
26229         }
26230         return {
26231             records : records,
26232             totalRecords : records.length
26233         };
26234     },
26235     // used when loading children.. @see loadDataFromChildren
26236     toLoadData: function(rec)
26237     {
26238         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26239         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26240         
26241     }
26242     
26243     
26244 });/*
26245  * Based on:
26246  * Ext JS Library 1.1.1
26247  * Copyright(c) 2006-2007, Ext JS, LLC.
26248  *
26249  * Originally Released Under LGPL - original licence link has changed is not relivant.
26250  *
26251  * Fork - LGPL
26252  * <script type="text/javascript">
26253  */
26254
26255
26256 /**
26257  * @class Roo.data.Tree
26258  * @extends Roo.util.Observable
26259  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26260  * in the tree have most standard DOM functionality.
26261  * @constructor
26262  * @param {Node} root (optional) The root node
26263  */
26264 Roo.data.Tree = function(root){
26265    this.nodeHash = {};
26266    /**
26267     * The root node for this tree
26268     * @type Node
26269     */
26270    this.root = null;
26271    if(root){
26272        this.setRootNode(root);
26273    }
26274    this.addEvents({
26275        /**
26276         * @event append
26277         * Fires when a new child node is appended to a node in this tree.
26278         * @param {Tree} tree The owner tree
26279         * @param {Node} parent The parent node
26280         * @param {Node} node The newly appended node
26281         * @param {Number} index The index of the newly appended node
26282         */
26283        "append" : true,
26284        /**
26285         * @event remove
26286         * Fires when a child node is removed from a node in this tree.
26287         * @param {Tree} tree The owner tree
26288         * @param {Node} parent The parent node
26289         * @param {Node} node The child node removed
26290         */
26291        "remove" : true,
26292        /**
26293         * @event move
26294         * Fires when a node is moved to a new location in the tree
26295         * @param {Tree} tree The owner tree
26296         * @param {Node} node The node moved
26297         * @param {Node} oldParent The old parent of this node
26298         * @param {Node} newParent The new parent of this node
26299         * @param {Number} index The index it was moved to
26300         */
26301        "move" : true,
26302        /**
26303         * @event insert
26304         * Fires when a new child node is inserted in a node in this tree.
26305         * @param {Tree} tree The owner tree
26306         * @param {Node} parent The parent node
26307         * @param {Node} node The child node inserted
26308         * @param {Node} refNode The child node the node was inserted before
26309         */
26310        "insert" : true,
26311        /**
26312         * @event beforeappend
26313         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26314         * @param {Tree} tree The owner tree
26315         * @param {Node} parent The parent node
26316         * @param {Node} node The child node to be appended
26317         */
26318        "beforeappend" : true,
26319        /**
26320         * @event beforeremove
26321         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26322         * @param {Tree} tree The owner tree
26323         * @param {Node} parent The parent node
26324         * @param {Node} node The child node to be removed
26325         */
26326        "beforeremove" : true,
26327        /**
26328         * @event beforemove
26329         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26330         * @param {Tree} tree The owner tree
26331         * @param {Node} node The node being moved
26332         * @param {Node} oldParent The parent of the node
26333         * @param {Node} newParent The new parent the node is moving to
26334         * @param {Number} index The index it is being moved to
26335         */
26336        "beforemove" : true,
26337        /**
26338         * @event beforeinsert
26339         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26340         * @param {Tree} tree The owner tree
26341         * @param {Node} parent The parent node
26342         * @param {Node} node The child node to be inserted
26343         * @param {Node} refNode The child node the node is being inserted before
26344         */
26345        "beforeinsert" : true
26346    });
26347
26348     Roo.data.Tree.superclass.constructor.call(this);
26349 };
26350
26351 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26352     pathSeparator: "/",
26353
26354     proxyNodeEvent : function(){
26355         return this.fireEvent.apply(this, arguments);
26356     },
26357
26358     /**
26359      * Returns the root node for this tree.
26360      * @return {Node}
26361      */
26362     getRootNode : function(){
26363         return this.root;
26364     },
26365
26366     /**
26367      * Sets the root node for this tree.
26368      * @param {Node} node
26369      * @return {Node}
26370      */
26371     setRootNode : function(node){
26372         this.root = node;
26373         node.ownerTree = this;
26374         node.isRoot = true;
26375         this.registerNode(node);
26376         return node;
26377     },
26378
26379     /**
26380      * Gets a node in this tree by its id.
26381      * @param {String} id
26382      * @return {Node}
26383      */
26384     getNodeById : function(id){
26385         return this.nodeHash[id];
26386     },
26387
26388     registerNode : function(node){
26389         this.nodeHash[node.id] = node;
26390     },
26391
26392     unregisterNode : function(node){
26393         delete this.nodeHash[node.id];
26394     },
26395
26396     toString : function(){
26397         return "[Tree"+(this.id?" "+this.id:"")+"]";
26398     }
26399 });
26400
26401 /**
26402  * @class Roo.data.Node
26403  * @extends Roo.util.Observable
26404  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26405  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26406  * @constructor
26407  * @param {Object} attributes The attributes/config for the node
26408  */
26409 Roo.data.Node = function(attributes){
26410     /**
26411      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26412      * @type {Object}
26413      */
26414     this.attributes = attributes || {};
26415     this.leaf = this.attributes.leaf;
26416     /**
26417      * The node id. @type String
26418      */
26419     this.id = this.attributes.id;
26420     if(!this.id){
26421         this.id = Roo.id(null, "ynode-");
26422         this.attributes.id = this.id;
26423     }
26424      
26425     
26426     /**
26427      * All child nodes of this node. @type Array
26428      */
26429     this.childNodes = [];
26430     if(!this.childNodes.indexOf){ // indexOf is a must
26431         this.childNodes.indexOf = function(o){
26432             for(var i = 0, len = this.length; i < len; i++){
26433                 if(this[i] == o) {
26434                     return i;
26435                 }
26436             }
26437             return -1;
26438         };
26439     }
26440     /**
26441      * The parent node for this node. @type Node
26442      */
26443     this.parentNode = null;
26444     /**
26445      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26446      */
26447     this.firstChild = null;
26448     /**
26449      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26450      */
26451     this.lastChild = null;
26452     /**
26453      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26454      */
26455     this.previousSibling = null;
26456     /**
26457      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26458      */
26459     this.nextSibling = null;
26460
26461     this.addEvents({
26462        /**
26463         * @event append
26464         * Fires when a new child node is appended
26465         * @param {Tree} tree The owner tree
26466         * @param {Node} this This node
26467         * @param {Node} node The newly appended node
26468         * @param {Number} index The index of the newly appended node
26469         */
26470        "append" : true,
26471        /**
26472         * @event remove
26473         * Fires when a child node is removed
26474         * @param {Tree} tree The owner tree
26475         * @param {Node} this This node
26476         * @param {Node} node The removed node
26477         */
26478        "remove" : true,
26479        /**
26480         * @event move
26481         * Fires when this node is moved to a new location in the tree
26482         * @param {Tree} tree The owner tree
26483         * @param {Node} this This node
26484         * @param {Node} oldParent The old parent of this node
26485         * @param {Node} newParent The new parent of this node
26486         * @param {Number} index The index it was moved to
26487         */
26488        "move" : true,
26489        /**
26490         * @event insert
26491         * Fires when a new child node is inserted.
26492         * @param {Tree} tree The owner tree
26493         * @param {Node} this This node
26494         * @param {Node} node The child node inserted
26495         * @param {Node} refNode The child node the node was inserted before
26496         */
26497        "insert" : true,
26498        /**
26499         * @event beforeappend
26500         * Fires before a new child is appended, return false to cancel the append.
26501         * @param {Tree} tree The owner tree
26502         * @param {Node} this This node
26503         * @param {Node} node The child node to be appended
26504         */
26505        "beforeappend" : true,
26506        /**
26507         * @event beforeremove
26508         * Fires before a child is removed, return false to cancel the remove.
26509         * @param {Tree} tree The owner tree
26510         * @param {Node} this This node
26511         * @param {Node} node The child node to be removed
26512         */
26513        "beforeremove" : true,
26514        /**
26515         * @event beforemove
26516         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26517         * @param {Tree} tree The owner tree
26518         * @param {Node} this This node
26519         * @param {Node} oldParent The parent of this node
26520         * @param {Node} newParent The new parent this node is moving to
26521         * @param {Number} index The index it is being moved to
26522         */
26523        "beforemove" : true,
26524        /**
26525         * @event beforeinsert
26526         * Fires before a new child is inserted, return false to cancel the insert.
26527         * @param {Tree} tree The owner tree
26528         * @param {Node} this This node
26529         * @param {Node} node The child node to be inserted
26530         * @param {Node} refNode The child node the node is being inserted before
26531         */
26532        "beforeinsert" : true
26533    });
26534     this.listeners = this.attributes.listeners;
26535     Roo.data.Node.superclass.constructor.call(this);
26536 };
26537
26538 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26539     fireEvent : function(evtName){
26540         // first do standard event for this node
26541         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26542             return false;
26543         }
26544         // then bubble it up to the tree if the event wasn't cancelled
26545         var ot = this.getOwnerTree();
26546         if(ot){
26547             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26548                 return false;
26549             }
26550         }
26551         return true;
26552     },
26553
26554     /**
26555      * Returns true if this node is a leaf
26556      * @return {Boolean}
26557      */
26558     isLeaf : function(){
26559         return this.leaf === true;
26560     },
26561
26562     // private
26563     setFirstChild : function(node){
26564         this.firstChild = node;
26565     },
26566
26567     //private
26568     setLastChild : function(node){
26569         this.lastChild = node;
26570     },
26571
26572
26573     /**
26574      * Returns true if this node is the last child of its parent
26575      * @return {Boolean}
26576      */
26577     isLast : function(){
26578        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26579     },
26580
26581     /**
26582      * Returns true if this node is the first child of its parent
26583      * @return {Boolean}
26584      */
26585     isFirst : function(){
26586        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26587     },
26588
26589     hasChildNodes : function(){
26590         return !this.isLeaf() && this.childNodes.length > 0;
26591     },
26592
26593     /**
26594      * Insert node(s) as the last child node of this node.
26595      * @param {Node/Array} node The node or Array of nodes to append
26596      * @return {Node} The appended node if single append, or null if an array was passed
26597      */
26598     appendChild : function(node){
26599         var multi = false;
26600         if(node instanceof Array){
26601             multi = node;
26602         }else if(arguments.length > 1){
26603             multi = arguments;
26604         }
26605         
26606         // if passed an array or multiple args do them one by one
26607         if(multi){
26608             for(var i = 0, len = multi.length; i < len; i++) {
26609                 this.appendChild(multi[i]);
26610             }
26611         }else{
26612             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26613                 return false;
26614             }
26615             var index = this.childNodes.length;
26616             var oldParent = node.parentNode;
26617             // it's a move, make sure we move it cleanly
26618             if(oldParent){
26619                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26620                     return false;
26621                 }
26622                 oldParent.removeChild(node);
26623             }
26624             
26625             index = this.childNodes.length;
26626             if(index == 0){
26627                 this.setFirstChild(node);
26628             }
26629             this.childNodes.push(node);
26630             node.parentNode = this;
26631             var ps = this.childNodes[index-1];
26632             if(ps){
26633                 node.previousSibling = ps;
26634                 ps.nextSibling = node;
26635             }else{
26636                 node.previousSibling = null;
26637             }
26638             node.nextSibling = null;
26639             this.setLastChild(node);
26640             node.setOwnerTree(this.getOwnerTree());
26641             this.fireEvent("append", this.ownerTree, this, node, index);
26642             if(this.ownerTree) {
26643                 this.ownerTree.fireEvent("appendnode", this, node, index);
26644             }
26645             if(oldParent){
26646                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26647             }
26648             return node;
26649         }
26650     },
26651
26652     /**
26653      * Removes a child node from this node.
26654      * @param {Node} node The node to remove
26655      * @return {Node} The removed node
26656      */
26657     removeChild : function(node){
26658         var index = this.childNodes.indexOf(node);
26659         if(index == -1){
26660             return false;
26661         }
26662         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26663             return false;
26664         }
26665
26666         // remove it from childNodes collection
26667         this.childNodes.splice(index, 1);
26668
26669         // update siblings
26670         if(node.previousSibling){
26671             node.previousSibling.nextSibling = node.nextSibling;
26672         }
26673         if(node.nextSibling){
26674             node.nextSibling.previousSibling = node.previousSibling;
26675         }
26676
26677         // update child refs
26678         if(this.firstChild == node){
26679             this.setFirstChild(node.nextSibling);
26680         }
26681         if(this.lastChild == node){
26682             this.setLastChild(node.previousSibling);
26683         }
26684
26685         node.setOwnerTree(null);
26686         // clear any references from the node
26687         node.parentNode = null;
26688         node.previousSibling = null;
26689         node.nextSibling = null;
26690         this.fireEvent("remove", this.ownerTree, this, node);
26691         return node;
26692     },
26693
26694     /**
26695      * Inserts the first node before the second node in this nodes childNodes collection.
26696      * @param {Node} node The node to insert
26697      * @param {Node} refNode The node to insert before (if null the node is appended)
26698      * @return {Node} The inserted node
26699      */
26700     insertBefore : function(node, refNode){
26701         if(!refNode){ // like standard Dom, refNode can be null for append
26702             return this.appendChild(node);
26703         }
26704         // nothing to do
26705         if(node == refNode){
26706             return false;
26707         }
26708
26709         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26710             return false;
26711         }
26712         var index = this.childNodes.indexOf(refNode);
26713         var oldParent = node.parentNode;
26714         var refIndex = index;
26715
26716         // when moving internally, indexes will change after remove
26717         if(oldParent == this && this.childNodes.indexOf(node) < index){
26718             refIndex--;
26719         }
26720
26721         // it's a move, make sure we move it cleanly
26722         if(oldParent){
26723             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26724                 return false;
26725             }
26726             oldParent.removeChild(node);
26727         }
26728         if(refIndex == 0){
26729             this.setFirstChild(node);
26730         }
26731         this.childNodes.splice(refIndex, 0, node);
26732         node.parentNode = this;
26733         var ps = this.childNodes[refIndex-1];
26734         if(ps){
26735             node.previousSibling = ps;
26736             ps.nextSibling = node;
26737         }else{
26738             node.previousSibling = null;
26739         }
26740         node.nextSibling = refNode;
26741         refNode.previousSibling = node;
26742         node.setOwnerTree(this.getOwnerTree());
26743         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26744         if(oldParent){
26745             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26746         }
26747         return node;
26748     },
26749
26750     /**
26751      * Returns the child node at the specified index.
26752      * @param {Number} index
26753      * @return {Node}
26754      */
26755     item : function(index){
26756         return this.childNodes[index];
26757     },
26758
26759     /**
26760      * Replaces one child node in this node with another.
26761      * @param {Node} newChild The replacement node
26762      * @param {Node} oldChild The node to replace
26763      * @return {Node} The replaced node
26764      */
26765     replaceChild : function(newChild, oldChild){
26766         this.insertBefore(newChild, oldChild);
26767         this.removeChild(oldChild);
26768         return oldChild;
26769     },
26770
26771     /**
26772      * Returns the index of a child node
26773      * @param {Node} node
26774      * @return {Number} The index of the node or -1 if it was not found
26775      */
26776     indexOf : function(child){
26777         return this.childNodes.indexOf(child);
26778     },
26779
26780     /**
26781      * Returns the tree this node is in.
26782      * @return {Tree}
26783      */
26784     getOwnerTree : function(){
26785         // if it doesn't have one, look for one
26786         if(!this.ownerTree){
26787             var p = this;
26788             while(p){
26789                 if(p.ownerTree){
26790                     this.ownerTree = p.ownerTree;
26791                     break;
26792                 }
26793                 p = p.parentNode;
26794             }
26795         }
26796         return this.ownerTree;
26797     },
26798
26799     /**
26800      * Returns depth of this node (the root node has a depth of 0)
26801      * @return {Number}
26802      */
26803     getDepth : function(){
26804         var depth = 0;
26805         var p = this;
26806         while(p.parentNode){
26807             ++depth;
26808             p = p.parentNode;
26809         }
26810         return depth;
26811     },
26812
26813     // private
26814     setOwnerTree : function(tree){
26815         // if it's move, we need to update everyone
26816         if(tree != this.ownerTree){
26817             if(this.ownerTree){
26818                 this.ownerTree.unregisterNode(this);
26819             }
26820             this.ownerTree = tree;
26821             var cs = this.childNodes;
26822             for(var i = 0, len = cs.length; i < len; i++) {
26823                 cs[i].setOwnerTree(tree);
26824             }
26825             if(tree){
26826                 tree.registerNode(this);
26827             }
26828         }
26829     },
26830
26831     /**
26832      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26833      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26834      * @return {String} The path
26835      */
26836     getPath : function(attr){
26837         attr = attr || "id";
26838         var p = this.parentNode;
26839         var b = [this.attributes[attr]];
26840         while(p){
26841             b.unshift(p.attributes[attr]);
26842             p = p.parentNode;
26843         }
26844         var sep = this.getOwnerTree().pathSeparator;
26845         return sep + b.join(sep);
26846     },
26847
26848     /**
26849      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26850      * function call will be the scope provided or the current node. The arguments to the function
26851      * will be the args provided or the current node. If the function returns false at any point,
26852      * the bubble is stopped.
26853      * @param {Function} fn The function to call
26854      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26855      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26856      */
26857     bubble : function(fn, scope, args){
26858         var p = this;
26859         while(p){
26860             if(fn.call(scope || p, args || p) === false){
26861                 break;
26862             }
26863             p = p.parentNode;
26864         }
26865     },
26866
26867     /**
26868      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26869      * function call will be the scope provided or the current node. The arguments to the function
26870      * will be the args provided or the current node. If the function returns false at any point,
26871      * the cascade is stopped on that branch.
26872      * @param {Function} fn The function to call
26873      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26874      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26875      */
26876     cascade : function(fn, scope, args){
26877         if(fn.call(scope || this, args || this) !== false){
26878             var cs = this.childNodes;
26879             for(var i = 0, len = cs.length; i < len; i++) {
26880                 cs[i].cascade(fn, scope, args);
26881             }
26882         }
26883     },
26884
26885     /**
26886      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26887      * function call will be the scope provided or the current node. The arguments to the function
26888      * will be the args provided or the current node. If the function returns false at any point,
26889      * the iteration stops.
26890      * @param {Function} fn The function to call
26891      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26892      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26893      */
26894     eachChild : function(fn, scope, args){
26895         var cs = this.childNodes;
26896         for(var i = 0, len = cs.length; i < len; i++) {
26897                 if(fn.call(scope || this, args || cs[i]) === false){
26898                     break;
26899                 }
26900         }
26901     },
26902
26903     /**
26904      * Finds the first child that has the attribute with the specified value.
26905      * @param {String} attribute The attribute name
26906      * @param {Mixed} value The value to search for
26907      * @return {Node} The found child or null if none was found
26908      */
26909     findChild : function(attribute, value){
26910         var cs = this.childNodes;
26911         for(var i = 0, len = cs.length; i < len; i++) {
26912                 if(cs[i].attributes[attribute] == value){
26913                     return cs[i];
26914                 }
26915         }
26916         return null;
26917     },
26918
26919     /**
26920      * Finds the first child by a custom function. The child matches if the function passed
26921      * returns true.
26922      * @param {Function} fn
26923      * @param {Object} scope (optional)
26924      * @return {Node} The found child or null if none was found
26925      */
26926     findChildBy : function(fn, scope){
26927         var cs = this.childNodes;
26928         for(var i = 0, len = cs.length; i < len; i++) {
26929                 if(fn.call(scope||cs[i], cs[i]) === true){
26930                     return cs[i];
26931                 }
26932         }
26933         return null;
26934     },
26935
26936     /**
26937      * Sorts this nodes children using the supplied sort function
26938      * @param {Function} fn
26939      * @param {Object} scope (optional)
26940      */
26941     sort : function(fn, scope){
26942         var cs = this.childNodes;
26943         var len = cs.length;
26944         if(len > 0){
26945             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26946             cs.sort(sortFn);
26947             for(var i = 0; i < len; i++){
26948                 var n = cs[i];
26949                 n.previousSibling = cs[i-1];
26950                 n.nextSibling = cs[i+1];
26951                 if(i == 0){
26952                     this.setFirstChild(n);
26953                 }
26954                 if(i == len-1){
26955                     this.setLastChild(n);
26956                 }
26957             }
26958         }
26959     },
26960
26961     /**
26962      * Returns true if this node is an ancestor (at any point) of the passed node.
26963      * @param {Node} node
26964      * @return {Boolean}
26965      */
26966     contains : function(node){
26967         return node.isAncestor(this);
26968     },
26969
26970     /**
26971      * Returns true if the passed node is an ancestor (at any point) of this node.
26972      * @param {Node} node
26973      * @return {Boolean}
26974      */
26975     isAncestor : function(node){
26976         var p = this.parentNode;
26977         while(p){
26978             if(p == node){
26979                 return true;
26980             }
26981             p = p.parentNode;
26982         }
26983         return false;
26984     },
26985
26986     toString : function(){
26987         return "[Node"+(this.id?" "+this.id:"")+"]";
26988     }
26989 });/*
26990  * Based on:
26991  * Ext JS Library 1.1.1
26992  * Copyright(c) 2006-2007, Ext JS, LLC.
26993  *
26994  * Originally Released Under LGPL - original licence link has changed is not relivant.
26995  *
26996  * Fork - LGPL
26997  * <script type="text/javascript">
26998  */
26999
27000
27001 /**
27002  * @class Roo.Shadow
27003  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27004  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27005  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27006  * @constructor
27007  * Create a new Shadow
27008  * @param {Object} config The config object
27009  */
27010 Roo.Shadow = function(config){
27011     Roo.apply(this, config);
27012     if(typeof this.mode != "string"){
27013         this.mode = this.defaultMode;
27014     }
27015     var o = this.offset, a = {h: 0};
27016     var rad = Math.floor(this.offset/2);
27017     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27018         case "drop":
27019             a.w = 0;
27020             a.l = a.t = o;
27021             a.t -= 1;
27022             if(Roo.isIE){
27023                 a.l -= this.offset + rad;
27024                 a.t -= this.offset + rad;
27025                 a.w -= rad;
27026                 a.h -= rad;
27027                 a.t += 1;
27028             }
27029         break;
27030         case "sides":
27031             a.w = (o*2);
27032             a.l = -o;
27033             a.t = o-1;
27034             if(Roo.isIE){
27035                 a.l -= (this.offset - rad);
27036                 a.t -= this.offset + rad;
27037                 a.l += 1;
27038                 a.w -= (this.offset - rad)*2;
27039                 a.w -= rad + 1;
27040                 a.h -= 1;
27041             }
27042         break;
27043         case "frame":
27044             a.w = a.h = (o*2);
27045             a.l = a.t = -o;
27046             a.t += 1;
27047             a.h -= 2;
27048             if(Roo.isIE){
27049                 a.l -= (this.offset - rad);
27050                 a.t -= (this.offset - rad);
27051                 a.l += 1;
27052                 a.w -= (this.offset + rad + 1);
27053                 a.h -= (this.offset + rad);
27054                 a.h += 1;
27055             }
27056         break;
27057     };
27058
27059     this.adjusts = a;
27060 };
27061
27062 Roo.Shadow.prototype = {
27063     /**
27064      * @cfg {String} mode
27065      * The shadow display mode.  Supports the following options:<br />
27066      * sides: Shadow displays on both sides and bottom only<br />
27067      * frame: Shadow displays equally on all four sides<br />
27068      * drop: Traditional bottom-right drop shadow (default)
27069      */
27070     mode: false,
27071     /**
27072      * @cfg {String} offset
27073      * The number of pixels to offset the shadow from the element (defaults to 4)
27074      */
27075     offset: 4,
27076
27077     // private
27078     defaultMode: "drop",
27079
27080     /**
27081      * Displays the shadow under the target element
27082      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27083      */
27084     show : function(target){
27085         target = Roo.get(target);
27086         if(!this.el){
27087             this.el = Roo.Shadow.Pool.pull();
27088             if(this.el.dom.nextSibling != target.dom){
27089                 this.el.insertBefore(target);
27090             }
27091         }
27092         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27093         if(Roo.isIE){
27094             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27095         }
27096         this.realign(
27097             target.getLeft(true),
27098             target.getTop(true),
27099             target.getWidth(),
27100             target.getHeight()
27101         );
27102         this.el.dom.style.display = "block";
27103     },
27104
27105     /**
27106      * Returns true if the shadow is visible, else false
27107      */
27108     isVisible : function(){
27109         return this.el ? true : false;  
27110     },
27111
27112     /**
27113      * Direct alignment when values are already available. Show must be called at least once before
27114      * calling this method to ensure it is initialized.
27115      * @param {Number} left The target element left position
27116      * @param {Number} top The target element top position
27117      * @param {Number} width The target element width
27118      * @param {Number} height The target element height
27119      */
27120     realign : function(l, t, w, h){
27121         if(!this.el){
27122             return;
27123         }
27124         var a = this.adjusts, d = this.el.dom, s = d.style;
27125         var iea = 0;
27126         s.left = (l+a.l)+"px";
27127         s.top = (t+a.t)+"px";
27128         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27129  
27130         if(s.width != sws || s.height != shs){
27131             s.width = sws;
27132             s.height = shs;
27133             if(!Roo.isIE){
27134                 var cn = d.childNodes;
27135                 var sww = Math.max(0, (sw-12))+"px";
27136                 cn[0].childNodes[1].style.width = sww;
27137                 cn[1].childNodes[1].style.width = sww;
27138                 cn[2].childNodes[1].style.width = sww;
27139                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27140             }
27141         }
27142     },
27143
27144     /**
27145      * Hides this shadow
27146      */
27147     hide : function(){
27148         if(this.el){
27149             this.el.dom.style.display = "none";
27150             Roo.Shadow.Pool.push(this.el);
27151             delete this.el;
27152         }
27153     },
27154
27155     /**
27156      * Adjust the z-index of this shadow
27157      * @param {Number} zindex The new z-index
27158      */
27159     setZIndex : function(z){
27160         this.zIndex = z;
27161         if(this.el){
27162             this.el.setStyle("z-index", z);
27163         }
27164     }
27165 };
27166
27167 // Private utility class that manages the internal Shadow cache
27168 Roo.Shadow.Pool = function(){
27169     var p = [];
27170     var markup = Roo.isIE ?
27171                  '<div class="x-ie-shadow"></div>' :
27172                  '<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>';
27173     return {
27174         pull : function(){
27175             var sh = p.shift();
27176             if(!sh){
27177                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27178                 sh.autoBoxAdjust = false;
27179             }
27180             return sh;
27181         },
27182
27183         push : function(sh){
27184             p.push(sh);
27185         }
27186     };
27187 }();/*
27188  * Based on:
27189  * Ext JS Library 1.1.1
27190  * Copyright(c) 2006-2007, Ext JS, LLC.
27191  *
27192  * Originally Released Under LGPL - original licence link has changed is not relivant.
27193  *
27194  * Fork - LGPL
27195  * <script type="text/javascript">
27196  */
27197
27198
27199 /**
27200  * @class Roo.SplitBar
27201  * @extends Roo.util.Observable
27202  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27203  * <br><br>
27204  * Usage:
27205  * <pre><code>
27206 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27207                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27208 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27209 split.minSize = 100;
27210 split.maxSize = 600;
27211 split.animate = true;
27212 split.on('moved', splitterMoved);
27213 </code></pre>
27214  * @constructor
27215  * Create a new SplitBar
27216  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27217  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27218  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27219  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27220                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27221                         position of the SplitBar).
27222  */
27223 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27224     
27225     /** @private */
27226     this.el = Roo.get(dragElement, true);
27227     this.el.dom.unselectable = "on";
27228     /** @private */
27229     this.resizingEl = Roo.get(resizingElement, true);
27230
27231     /**
27232      * @private
27233      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27234      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27235      * @type Number
27236      */
27237     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27238     
27239     /**
27240      * The minimum size of the resizing element. (Defaults to 0)
27241      * @type Number
27242      */
27243     this.minSize = 0;
27244     
27245     /**
27246      * The maximum size of the resizing element. (Defaults to 2000)
27247      * @type Number
27248      */
27249     this.maxSize = 2000;
27250     
27251     /**
27252      * Whether to animate the transition to the new size
27253      * @type Boolean
27254      */
27255     this.animate = false;
27256     
27257     /**
27258      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27259      * @type Boolean
27260      */
27261     this.useShim = false;
27262     
27263     /** @private */
27264     this.shim = null;
27265     
27266     if(!existingProxy){
27267         /** @private */
27268         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27269     }else{
27270         this.proxy = Roo.get(existingProxy).dom;
27271     }
27272     /** @private */
27273     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27274     
27275     /** @private */
27276     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27277     
27278     /** @private */
27279     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27280     
27281     /** @private */
27282     this.dragSpecs = {};
27283     
27284     /**
27285      * @private The adapter to use to positon and resize elements
27286      */
27287     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27288     this.adapter.init(this);
27289     
27290     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27291         /** @private */
27292         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27293         this.el.addClass("x-splitbar-h");
27294     }else{
27295         /** @private */
27296         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27297         this.el.addClass("x-splitbar-v");
27298     }
27299     
27300     this.addEvents({
27301         /**
27302          * @event resize
27303          * Fires when the splitter is moved (alias for {@link #event-moved})
27304          * @param {Roo.SplitBar} this
27305          * @param {Number} newSize the new width or height
27306          */
27307         "resize" : true,
27308         /**
27309          * @event moved
27310          * Fires when the splitter is moved
27311          * @param {Roo.SplitBar} this
27312          * @param {Number} newSize the new width or height
27313          */
27314         "moved" : true,
27315         /**
27316          * @event beforeresize
27317          * Fires before the splitter is dragged
27318          * @param {Roo.SplitBar} this
27319          */
27320         "beforeresize" : true,
27321
27322         "beforeapply" : true
27323     });
27324
27325     Roo.util.Observable.call(this);
27326 };
27327
27328 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27329     onStartProxyDrag : function(x, y){
27330         this.fireEvent("beforeresize", this);
27331         if(!this.overlay){
27332             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27333             o.unselectable();
27334             o.enableDisplayMode("block");
27335             // all splitbars share the same overlay
27336             Roo.SplitBar.prototype.overlay = o;
27337         }
27338         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27339         this.overlay.show();
27340         Roo.get(this.proxy).setDisplayed("block");
27341         var size = this.adapter.getElementSize(this);
27342         this.activeMinSize = this.getMinimumSize();;
27343         this.activeMaxSize = this.getMaximumSize();;
27344         var c1 = size - this.activeMinSize;
27345         var c2 = Math.max(this.activeMaxSize - size, 0);
27346         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27347             this.dd.resetConstraints();
27348             this.dd.setXConstraint(
27349                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27350                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27351             );
27352             this.dd.setYConstraint(0, 0);
27353         }else{
27354             this.dd.resetConstraints();
27355             this.dd.setXConstraint(0, 0);
27356             this.dd.setYConstraint(
27357                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27358                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27359             );
27360          }
27361         this.dragSpecs.startSize = size;
27362         this.dragSpecs.startPoint = [x, y];
27363         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27364     },
27365     
27366     /** 
27367      * @private Called after the drag operation by the DDProxy
27368      */
27369     onEndProxyDrag : function(e){
27370         Roo.get(this.proxy).setDisplayed(false);
27371         var endPoint = Roo.lib.Event.getXY(e);
27372         if(this.overlay){
27373             this.overlay.hide();
27374         }
27375         var newSize;
27376         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27377             newSize = this.dragSpecs.startSize + 
27378                 (this.placement == Roo.SplitBar.LEFT ?
27379                     endPoint[0] - this.dragSpecs.startPoint[0] :
27380                     this.dragSpecs.startPoint[0] - endPoint[0]
27381                 );
27382         }else{
27383             newSize = this.dragSpecs.startSize + 
27384                 (this.placement == Roo.SplitBar.TOP ?
27385                     endPoint[1] - this.dragSpecs.startPoint[1] :
27386                     this.dragSpecs.startPoint[1] - endPoint[1]
27387                 );
27388         }
27389         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27390         if(newSize != this.dragSpecs.startSize){
27391             if(this.fireEvent('beforeapply', this, newSize) !== false){
27392                 this.adapter.setElementSize(this, newSize);
27393                 this.fireEvent("moved", this, newSize);
27394                 this.fireEvent("resize", this, newSize);
27395             }
27396         }
27397     },
27398     
27399     /**
27400      * Get the adapter this SplitBar uses
27401      * @return The adapter object
27402      */
27403     getAdapter : function(){
27404         return this.adapter;
27405     },
27406     
27407     /**
27408      * Set the adapter this SplitBar uses
27409      * @param {Object} adapter A SplitBar adapter object
27410      */
27411     setAdapter : function(adapter){
27412         this.adapter = adapter;
27413         this.adapter.init(this);
27414     },
27415     
27416     /**
27417      * Gets the minimum size for the resizing element
27418      * @return {Number} The minimum size
27419      */
27420     getMinimumSize : function(){
27421         return this.minSize;
27422     },
27423     
27424     /**
27425      * Sets the minimum size for the resizing element
27426      * @param {Number} minSize The minimum size
27427      */
27428     setMinimumSize : function(minSize){
27429         this.minSize = minSize;
27430     },
27431     
27432     /**
27433      * Gets the maximum size for the resizing element
27434      * @return {Number} The maximum size
27435      */
27436     getMaximumSize : function(){
27437         return this.maxSize;
27438     },
27439     
27440     /**
27441      * Sets the maximum size for the resizing element
27442      * @param {Number} maxSize The maximum size
27443      */
27444     setMaximumSize : function(maxSize){
27445         this.maxSize = maxSize;
27446     },
27447     
27448     /**
27449      * Sets the initialize size for the resizing element
27450      * @param {Number} size The initial size
27451      */
27452     setCurrentSize : function(size){
27453         var oldAnimate = this.animate;
27454         this.animate = false;
27455         this.adapter.setElementSize(this, size);
27456         this.animate = oldAnimate;
27457     },
27458     
27459     /**
27460      * Destroy this splitbar. 
27461      * @param {Boolean} removeEl True to remove the element
27462      */
27463     destroy : function(removeEl){
27464         if(this.shim){
27465             this.shim.remove();
27466         }
27467         this.dd.unreg();
27468         this.proxy.parentNode.removeChild(this.proxy);
27469         if(removeEl){
27470             this.el.remove();
27471         }
27472     }
27473 });
27474
27475 /**
27476  * @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.
27477  */
27478 Roo.SplitBar.createProxy = function(dir){
27479     var proxy = new Roo.Element(document.createElement("div"));
27480     proxy.unselectable();
27481     var cls = 'x-splitbar-proxy';
27482     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27483     document.body.appendChild(proxy.dom);
27484     return proxy.dom;
27485 };
27486
27487 /** 
27488  * @class Roo.SplitBar.BasicLayoutAdapter
27489  * Default Adapter. It assumes the splitter and resizing element are not positioned
27490  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27491  */
27492 Roo.SplitBar.BasicLayoutAdapter = function(){
27493 };
27494
27495 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27496     // do nothing for now
27497     init : function(s){
27498     
27499     },
27500     /**
27501      * Called before drag operations to get the current size of the resizing element. 
27502      * @param {Roo.SplitBar} s The SplitBar using this adapter
27503      */
27504      getElementSize : function(s){
27505         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27506             return s.resizingEl.getWidth();
27507         }else{
27508             return s.resizingEl.getHeight();
27509         }
27510     },
27511     
27512     /**
27513      * Called after drag operations to set the size of the resizing element.
27514      * @param {Roo.SplitBar} s The SplitBar using this adapter
27515      * @param {Number} newSize The new size to set
27516      * @param {Function} onComplete A function to be invoked when resizing is complete
27517      */
27518     setElementSize : function(s, newSize, onComplete){
27519         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27520             if(!s.animate){
27521                 s.resizingEl.setWidth(newSize);
27522                 if(onComplete){
27523                     onComplete(s, newSize);
27524                 }
27525             }else{
27526                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27527             }
27528         }else{
27529             
27530             if(!s.animate){
27531                 s.resizingEl.setHeight(newSize);
27532                 if(onComplete){
27533                     onComplete(s, newSize);
27534                 }
27535             }else{
27536                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27537             }
27538         }
27539     }
27540 };
27541
27542 /** 
27543  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27544  * @extends Roo.SplitBar.BasicLayoutAdapter
27545  * Adapter that  moves the splitter element to align with the resized sizing element. 
27546  * Used with an absolute positioned SplitBar.
27547  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27548  * document.body, make sure you assign an id to the body element.
27549  */
27550 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27551     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27552     this.container = Roo.get(container);
27553 };
27554
27555 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27556     init : function(s){
27557         this.basic.init(s);
27558     },
27559     
27560     getElementSize : function(s){
27561         return this.basic.getElementSize(s);
27562     },
27563     
27564     setElementSize : function(s, newSize, onComplete){
27565         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27566     },
27567     
27568     moveSplitter : function(s){
27569         var yes = Roo.SplitBar;
27570         switch(s.placement){
27571             case yes.LEFT:
27572                 s.el.setX(s.resizingEl.getRight());
27573                 break;
27574             case yes.RIGHT:
27575                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27576                 break;
27577             case yes.TOP:
27578                 s.el.setY(s.resizingEl.getBottom());
27579                 break;
27580             case yes.BOTTOM:
27581                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27582                 break;
27583         }
27584     }
27585 };
27586
27587 /**
27588  * Orientation constant - Create a vertical SplitBar
27589  * @static
27590  * @type Number
27591  */
27592 Roo.SplitBar.VERTICAL = 1;
27593
27594 /**
27595  * Orientation constant - Create a horizontal SplitBar
27596  * @static
27597  * @type Number
27598  */
27599 Roo.SplitBar.HORIZONTAL = 2;
27600
27601 /**
27602  * Placement constant - The resizing element is to the left of the splitter element
27603  * @static
27604  * @type Number
27605  */
27606 Roo.SplitBar.LEFT = 1;
27607
27608 /**
27609  * Placement constant - The resizing element is to the right of the splitter element
27610  * @static
27611  * @type Number
27612  */
27613 Roo.SplitBar.RIGHT = 2;
27614
27615 /**
27616  * Placement constant - The resizing element is positioned above the splitter element
27617  * @static
27618  * @type Number
27619  */
27620 Roo.SplitBar.TOP = 3;
27621
27622 /**
27623  * Placement constant - The resizing element is positioned under splitter element
27624  * @static
27625  * @type Number
27626  */
27627 Roo.SplitBar.BOTTOM = 4;
27628 /*
27629  * Based on:
27630  * Ext JS Library 1.1.1
27631  * Copyright(c) 2006-2007, Ext JS, LLC.
27632  *
27633  * Originally Released Under LGPL - original licence link has changed is not relivant.
27634  *
27635  * Fork - LGPL
27636  * <script type="text/javascript">
27637  */
27638
27639 /**
27640  * @class Roo.View
27641  * @extends Roo.util.Observable
27642  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27643  * This class also supports single and multi selection modes. <br>
27644  * Create a data model bound view:
27645  <pre><code>
27646  var store = new Roo.data.Store(...);
27647
27648  var view = new Roo.View({
27649     el : "my-element",
27650     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27651  
27652     singleSelect: true,
27653     selectedClass: "ydataview-selected",
27654     store: store
27655  });
27656
27657  // listen for node click?
27658  view.on("click", function(vw, index, node, e){
27659  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27660  });
27661
27662  // load XML data
27663  dataModel.load("foobar.xml");
27664  </code></pre>
27665  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27666  * <br><br>
27667  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27668  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27669  * 
27670  * Note: old style constructor is still suported (container, template, config)
27671  * 
27672  * @constructor
27673  * Create a new View
27674  * @param {Object} config The config object
27675  * 
27676  */
27677 Roo.View = function(config, depreciated_tpl, depreciated_config){
27678     
27679     this.parent = false;
27680     
27681     if (typeof(depreciated_tpl) == 'undefined') {
27682         // new way.. - universal constructor.
27683         Roo.apply(this, config);
27684         this.el  = Roo.get(this.el);
27685     } else {
27686         // old format..
27687         this.el  = Roo.get(config);
27688         this.tpl = depreciated_tpl;
27689         Roo.apply(this, depreciated_config);
27690     }
27691     this.wrapEl  = this.el.wrap().wrap();
27692     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27693     
27694     
27695     if(typeof(this.tpl) == "string"){
27696         this.tpl = new Roo.Template(this.tpl);
27697     } else {
27698         // support xtype ctors..
27699         this.tpl = new Roo.factory(this.tpl, Roo);
27700     }
27701     
27702     
27703     this.tpl.compile();
27704     
27705     /** @private */
27706     this.addEvents({
27707         /**
27708          * @event beforeclick
27709          * Fires before a click is processed. Returns false to cancel the default action.
27710          * @param {Roo.View} this
27711          * @param {Number} index The index of the target node
27712          * @param {HTMLElement} node The target node
27713          * @param {Roo.EventObject} e The raw event object
27714          */
27715             "beforeclick" : true,
27716         /**
27717          * @event click
27718          * Fires when a template node is clicked.
27719          * @param {Roo.View} this
27720          * @param {Number} index The index of the target node
27721          * @param {HTMLElement} node The target node
27722          * @param {Roo.EventObject} e The raw event object
27723          */
27724             "click" : true,
27725         /**
27726          * @event dblclick
27727          * Fires when a template node is double clicked.
27728          * @param {Roo.View} this
27729          * @param {Number} index The index of the target node
27730          * @param {HTMLElement} node The target node
27731          * @param {Roo.EventObject} e The raw event object
27732          */
27733             "dblclick" : true,
27734         /**
27735          * @event contextmenu
27736          * Fires when a template node is right clicked.
27737          * @param {Roo.View} this
27738          * @param {Number} index The index of the target node
27739          * @param {HTMLElement} node The target node
27740          * @param {Roo.EventObject} e The raw event object
27741          */
27742             "contextmenu" : true,
27743         /**
27744          * @event selectionchange
27745          * Fires when the selected nodes change.
27746          * @param {Roo.View} this
27747          * @param {Array} selections Array of the selected nodes
27748          */
27749             "selectionchange" : true,
27750     
27751         /**
27752          * @event beforeselect
27753          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27754          * @param {Roo.View} this
27755          * @param {HTMLElement} node The node to be selected
27756          * @param {Array} selections Array of currently selected nodes
27757          */
27758             "beforeselect" : true,
27759         /**
27760          * @event preparedata
27761          * Fires on every row to render, to allow you to change the data.
27762          * @param {Roo.View} this
27763          * @param {Object} data to be rendered (change this)
27764          */
27765           "preparedata" : true
27766           
27767           
27768         });
27769
27770
27771
27772     this.el.on({
27773         "click": this.onClick,
27774         "dblclick": this.onDblClick,
27775         "contextmenu": this.onContextMenu,
27776         scope:this
27777     });
27778
27779     this.selections = [];
27780     this.nodes = [];
27781     this.cmp = new Roo.CompositeElementLite([]);
27782     if(this.store){
27783         this.store = Roo.factory(this.store, Roo.data);
27784         this.setStore(this.store, true);
27785     }
27786     
27787     if ( this.footer && this.footer.xtype) {
27788            
27789          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27790         
27791         this.footer.dataSource = this.store;
27792         this.footer.container = fctr;
27793         this.footer = Roo.factory(this.footer, Roo);
27794         fctr.insertFirst(this.el);
27795         
27796         // this is a bit insane - as the paging toolbar seems to detach the el..
27797 //        dom.parentNode.parentNode.parentNode
27798          // they get detached?
27799     }
27800     
27801     
27802     Roo.View.superclass.constructor.call(this);
27803     
27804     
27805 };
27806
27807 Roo.extend(Roo.View, Roo.util.Observable, {
27808     
27809      /**
27810      * @cfg {Roo.data.Store} store Data store to load data from.
27811      */
27812     store : false,
27813     
27814     /**
27815      * @cfg {String|Roo.Element} el The container element.
27816      */
27817     el : '',
27818     
27819     /**
27820      * @cfg {String|Roo.Template} tpl The template used by this View 
27821      */
27822     tpl : false,
27823     /**
27824      * @cfg {String} dataName the named area of the template to use as the data area
27825      *                          Works with domtemplates roo-name="name"
27826      */
27827     dataName: false,
27828     /**
27829      * @cfg {String} selectedClass The css class to add to selected nodes
27830      */
27831     selectedClass : "x-view-selected",
27832      /**
27833      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27834      */
27835     emptyText : "",
27836     
27837     /**
27838      * @cfg {String} text to display on mask (default Loading)
27839      */
27840     mask : false,
27841     /**
27842      * @cfg {Boolean} multiSelect Allow multiple selection
27843      */
27844     multiSelect : false,
27845     /**
27846      * @cfg {Boolean} singleSelect Allow single selection
27847      */
27848     singleSelect:  false,
27849     
27850     /**
27851      * @cfg {Boolean} toggleSelect - selecting 
27852      */
27853     toggleSelect : false,
27854     
27855     /**
27856      * @cfg {Boolean} tickable - selecting 
27857      */
27858     tickable : false,
27859     
27860     /**
27861      * Returns the element this view is bound to.
27862      * @return {Roo.Element}
27863      */
27864     getEl : function(){
27865         return this.wrapEl;
27866     },
27867     
27868     
27869
27870     /**
27871      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27872      */
27873     refresh : function(){
27874         //Roo.log('refresh');
27875         var t = this.tpl;
27876         
27877         // if we are using something like 'domtemplate', then
27878         // the what gets used is:
27879         // t.applySubtemplate(NAME, data, wrapping data..)
27880         // the outer template then get' applied with
27881         //     the store 'extra data'
27882         // and the body get's added to the
27883         //      roo-name="data" node?
27884         //      <span class='roo-tpl-{name}'></span> ?????
27885         
27886         
27887         
27888         this.clearSelections();
27889         this.el.update("");
27890         var html = [];
27891         var records = this.store.getRange();
27892         if(records.length < 1) {
27893             
27894             // is this valid??  = should it render a template??
27895             
27896             this.el.update(this.emptyText);
27897             return;
27898         }
27899         var el = this.el;
27900         if (this.dataName) {
27901             this.el.update(t.apply(this.store.meta)); //????
27902             el = this.el.child('.roo-tpl-' + this.dataName);
27903         }
27904         
27905         for(var i = 0, len = records.length; i < len; i++){
27906             var data = this.prepareData(records[i].data, i, records[i]);
27907             this.fireEvent("preparedata", this, data, i, records[i]);
27908             
27909             var d = Roo.apply({}, data);
27910             
27911             if(this.tickable){
27912                 Roo.apply(d, {'roo-id' : Roo.id()});
27913                 
27914                 var _this = this;
27915             
27916                 Roo.each(this.parent.item, function(item){
27917                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27918                         return;
27919                     }
27920                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27921                 });
27922             }
27923             
27924             html[html.length] = Roo.util.Format.trim(
27925                 this.dataName ?
27926                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27927                     t.apply(d)
27928             );
27929         }
27930         
27931         
27932         
27933         el.update(html.join(""));
27934         this.nodes = el.dom.childNodes;
27935         this.updateIndexes(0);
27936     },
27937     
27938
27939     /**
27940      * Function to override to reformat the data that is sent to
27941      * the template for each node.
27942      * DEPRICATED - use the preparedata event handler.
27943      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27944      * a JSON object for an UpdateManager bound view).
27945      */
27946     prepareData : function(data, index, record)
27947     {
27948         this.fireEvent("preparedata", this, data, index, record);
27949         return data;
27950     },
27951
27952     onUpdate : function(ds, record){
27953         // Roo.log('on update');   
27954         this.clearSelections();
27955         var index = this.store.indexOf(record);
27956         var n = this.nodes[index];
27957         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27958         n.parentNode.removeChild(n);
27959         this.updateIndexes(index, index);
27960     },
27961
27962     
27963     
27964 // --------- FIXME     
27965     onAdd : function(ds, records, index)
27966     {
27967         //Roo.log(['on Add', ds, records, index] );        
27968         this.clearSelections();
27969         if(this.nodes.length == 0){
27970             this.refresh();
27971             return;
27972         }
27973         var n = this.nodes[index];
27974         for(var i = 0, len = records.length; i < len; i++){
27975             var d = this.prepareData(records[i].data, i, records[i]);
27976             if(n){
27977                 this.tpl.insertBefore(n, d);
27978             }else{
27979                 
27980                 this.tpl.append(this.el, d);
27981             }
27982         }
27983         this.updateIndexes(index);
27984     },
27985
27986     onRemove : function(ds, record, index){
27987        // Roo.log('onRemove');
27988         this.clearSelections();
27989         var el = this.dataName  ?
27990             this.el.child('.roo-tpl-' + this.dataName) :
27991             this.el; 
27992         
27993         el.dom.removeChild(this.nodes[index]);
27994         this.updateIndexes(index);
27995     },
27996
27997     /**
27998      * Refresh an individual node.
27999      * @param {Number} index
28000      */
28001     refreshNode : function(index){
28002         this.onUpdate(this.store, this.store.getAt(index));
28003     },
28004
28005     updateIndexes : function(startIndex, endIndex){
28006         var ns = this.nodes;
28007         startIndex = startIndex || 0;
28008         endIndex = endIndex || ns.length - 1;
28009         for(var i = startIndex; i <= endIndex; i++){
28010             ns[i].nodeIndex = i;
28011         }
28012     },
28013
28014     /**
28015      * Changes the data store this view uses and refresh the view.
28016      * @param {Store} store
28017      */
28018     setStore : function(store, initial){
28019         if(!initial && this.store){
28020             this.store.un("datachanged", this.refresh);
28021             this.store.un("add", this.onAdd);
28022             this.store.un("remove", this.onRemove);
28023             this.store.un("update", this.onUpdate);
28024             this.store.un("clear", this.refresh);
28025             this.store.un("beforeload", this.onBeforeLoad);
28026             this.store.un("load", this.onLoad);
28027             this.store.un("loadexception", this.onLoad);
28028         }
28029         if(store){
28030           
28031             store.on("datachanged", this.refresh, this);
28032             store.on("add", this.onAdd, this);
28033             store.on("remove", this.onRemove, this);
28034             store.on("update", this.onUpdate, this);
28035             store.on("clear", this.refresh, this);
28036             store.on("beforeload", this.onBeforeLoad, this);
28037             store.on("load", this.onLoad, this);
28038             store.on("loadexception", this.onLoad, this);
28039         }
28040         
28041         if(store){
28042             this.refresh();
28043         }
28044     },
28045     /**
28046      * onbeforeLoad - masks the loading area.
28047      *
28048      */
28049     onBeforeLoad : function(store,opts)
28050     {
28051          //Roo.log('onBeforeLoad');   
28052         if (!opts.add) {
28053             this.el.update("");
28054         }
28055         this.el.mask(this.mask ? this.mask : "Loading" ); 
28056     },
28057     onLoad : function ()
28058     {
28059         this.el.unmask();
28060     },
28061     
28062
28063     /**
28064      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28065      * @param {HTMLElement} node
28066      * @return {HTMLElement} The template node
28067      */
28068     findItemFromChild : function(node){
28069         var el = this.dataName  ?
28070             this.el.child('.roo-tpl-' + this.dataName,true) :
28071             this.el.dom; 
28072         
28073         if(!node || node.parentNode == el){
28074                     return node;
28075             }
28076             var p = node.parentNode;
28077             while(p && p != el){
28078             if(p.parentNode == el){
28079                 return p;
28080             }
28081             p = p.parentNode;
28082         }
28083             return null;
28084     },
28085
28086     /** @ignore */
28087     onClick : function(e){
28088         var item = this.findItemFromChild(e.getTarget());
28089         if(item){
28090             var index = this.indexOf(item);
28091             if(this.onItemClick(item, index, e) !== false){
28092                 this.fireEvent("click", this, index, item, e);
28093             }
28094         }else{
28095             this.clearSelections();
28096         }
28097     },
28098
28099     /** @ignore */
28100     onContextMenu : function(e){
28101         var item = this.findItemFromChild(e.getTarget());
28102         if(item){
28103             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28104         }
28105     },
28106
28107     /** @ignore */
28108     onDblClick : function(e){
28109         var item = this.findItemFromChild(e.getTarget());
28110         if(item){
28111             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28112         }
28113     },
28114
28115     onItemClick : function(item, index, e)
28116     {
28117         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28118             return false;
28119         }
28120         if (this.toggleSelect) {
28121             var m = this.isSelected(item) ? 'unselect' : 'select';
28122             //Roo.log(m);
28123             var _t = this;
28124             _t[m](item, true, false);
28125             return true;
28126         }
28127         if(this.multiSelect || this.singleSelect){
28128             if(this.multiSelect && e.shiftKey && this.lastSelection){
28129                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28130             }else{
28131                 this.select(item, this.multiSelect && e.ctrlKey);
28132                 this.lastSelection = item;
28133             }
28134             
28135             if(!this.tickable){
28136                 e.preventDefault();
28137             }
28138             
28139         }
28140         return true;
28141     },
28142
28143     /**
28144      * Get the number of selected nodes.
28145      * @return {Number}
28146      */
28147     getSelectionCount : function(){
28148         return this.selections.length;
28149     },
28150
28151     /**
28152      * Get the currently selected nodes.
28153      * @return {Array} An array of HTMLElements
28154      */
28155     getSelectedNodes : function(){
28156         return this.selections;
28157     },
28158
28159     /**
28160      * Get the indexes of the selected nodes.
28161      * @return {Array}
28162      */
28163     getSelectedIndexes : function(){
28164         var indexes = [], s = this.selections;
28165         for(var i = 0, len = s.length; i < len; i++){
28166             indexes.push(s[i].nodeIndex);
28167         }
28168         return indexes;
28169     },
28170
28171     /**
28172      * Clear all selections
28173      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28174      */
28175     clearSelections : function(suppressEvent){
28176         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28177             this.cmp.elements = this.selections;
28178             this.cmp.removeClass(this.selectedClass);
28179             this.selections = [];
28180             if(!suppressEvent){
28181                 this.fireEvent("selectionchange", this, this.selections);
28182             }
28183         }
28184     },
28185
28186     /**
28187      * Returns true if the passed node is selected
28188      * @param {HTMLElement/Number} node The node or node index
28189      * @return {Boolean}
28190      */
28191     isSelected : function(node){
28192         var s = this.selections;
28193         if(s.length < 1){
28194             return false;
28195         }
28196         node = this.getNode(node);
28197         return s.indexOf(node) !== -1;
28198     },
28199
28200     /**
28201      * Selects nodes.
28202      * @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
28203      * @param {Boolean} keepExisting (optional) true to keep existing selections
28204      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28205      */
28206     select : function(nodeInfo, keepExisting, suppressEvent){
28207         if(nodeInfo instanceof Array){
28208             if(!keepExisting){
28209                 this.clearSelections(true);
28210             }
28211             for(var i = 0, len = nodeInfo.length; i < len; i++){
28212                 this.select(nodeInfo[i], true, true);
28213             }
28214             return;
28215         } 
28216         var node = this.getNode(nodeInfo);
28217         if(!node || this.isSelected(node)){
28218             return; // already selected.
28219         }
28220         if(!keepExisting){
28221             this.clearSelections(true);
28222         }
28223         
28224         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28225             Roo.fly(node).addClass(this.selectedClass);
28226             this.selections.push(node);
28227             if(!suppressEvent){
28228                 this.fireEvent("selectionchange", this, this.selections);
28229             }
28230         }
28231         
28232         
28233     },
28234       /**
28235      * Unselects nodes.
28236      * @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
28237      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28238      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28239      */
28240     unselect : function(nodeInfo, keepExisting, suppressEvent)
28241     {
28242         if(nodeInfo instanceof Array){
28243             Roo.each(this.selections, function(s) {
28244                 this.unselect(s, nodeInfo);
28245             }, this);
28246             return;
28247         }
28248         var node = this.getNode(nodeInfo);
28249         if(!node || !this.isSelected(node)){
28250             //Roo.log("not selected");
28251             return; // not selected.
28252         }
28253         // fireevent???
28254         var ns = [];
28255         Roo.each(this.selections, function(s) {
28256             if (s == node ) {
28257                 Roo.fly(node).removeClass(this.selectedClass);
28258
28259                 return;
28260             }
28261             ns.push(s);
28262         },this);
28263         
28264         this.selections= ns;
28265         this.fireEvent("selectionchange", this, this.selections);
28266     },
28267
28268     /**
28269      * Gets a template node.
28270      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28271      * @return {HTMLElement} The node or null if it wasn't found
28272      */
28273     getNode : function(nodeInfo){
28274         if(typeof nodeInfo == "string"){
28275             return document.getElementById(nodeInfo);
28276         }else if(typeof nodeInfo == "number"){
28277             return this.nodes[nodeInfo];
28278         }
28279         return nodeInfo;
28280     },
28281
28282     /**
28283      * Gets a range template nodes.
28284      * @param {Number} startIndex
28285      * @param {Number} endIndex
28286      * @return {Array} An array of nodes
28287      */
28288     getNodes : function(start, end){
28289         var ns = this.nodes;
28290         start = start || 0;
28291         end = typeof end == "undefined" ? ns.length - 1 : end;
28292         var nodes = [];
28293         if(start <= end){
28294             for(var i = start; i <= end; i++){
28295                 nodes.push(ns[i]);
28296             }
28297         } else{
28298             for(var i = start; i >= end; i--){
28299                 nodes.push(ns[i]);
28300             }
28301         }
28302         return nodes;
28303     },
28304
28305     /**
28306      * Finds the index of the passed node
28307      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28308      * @return {Number} The index of the node or -1
28309      */
28310     indexOf : function(node){
28311         node = this.getNode(node);
28312         if(typeof node.nodeIndex == "number"){
28313             return node.nodeIndex;
28314         }
28315         var ns = this.nodes;
28316         for(var i = 0, len = ns.length; i < len; i++){
28317             if(ns[i] == node){
28318                 return i;
28319             }
28320         }
28321         return -1;
28322     }
28323 });
28324 /*
28325  * Based on:
28326  * Ext JS Library 1.1.1
28327  * Copyright(c) 2006-2007, Ext JS, LLC.
28328  *
28329  * Originally Released Under LGPL - original licence link has changed is not relivant.
28330  *
28331  * Fork - LGPL
28332  * <script type="text/javascript">
28333  */
28334
28335 /**
28336  * @class Roo.JsonView
28337  * @extends Roo.View
28338  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28339 <pre><code>
28340 var view = new Roo.JsonView({
28341     container: "my-element",
28342     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28343     multiSelect: true, 
28344     jsonRoot: "data" 
28345 });
28346
28347 // listen for node click?
28348 view.on("click", function(vw, index, node, e){
28349     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28350 });
28351
28352 // direct load of JSON data
28353 view.load("foobar.php");
28354
28355 // Example from my blog list
28356 var tpl = new Roo.Template(
28357     '&lt;div class="entry"&gt;' +
28358     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28359     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28360     "&lt;/div&gt;&lt;hr /&gt;"
28361 );
28362
28363 var moreView = new Roo.JsonView({
28364     container :  "entry-list", 
28365     template : tpl,
28366     jsonRoot: "posts"
28367 });
28368 moreView.on("beforerender", this.sortEntries, this);
28369 moreView.load({
28370     url: "/blog/get-posts.php",
28371     params: "allposts=true",
28372     text: "Loading Blog Entries..."
28373 });
28374 </code></pre>
28375
28376 * Note: old code is supported with arguments : (container, template, config)
28377
28378
28379  * @constructor
28380  * Create a new JsonView
28381  * 
28382  * @param {Object} config The config object
28383  * 
28384  */
28385 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28386     
28387     
28388     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28389
28390     var um = this.el.getUpdateManager();
28391     um.setRenderer(this);
28392     um.on("update", this.onLoad, this);
28393     um.on("failure", this.onLoadException, this);
28394
28395     /**
28396      * @event beforerender
28397      * Fires before rendering of the downloaded JSON data.
28398      * @param {Roo.JsonView} this
28399      * @param {Object} data The JSON data loaded
28400      */
28401     /**
28402      * @event load
28403      * Fires when data is loaded.
28404      * @param {Roo.JsonView} this
28405      * @param {Object} data The JSON data loaded
28406      * @param {Object} response The raw Connect response object
28407      */
28408     /**
28409      * @event loadexception
28410      * Fires when loading fails.
28411      * @param {Roo.JsonView} this
28412      * @param {Object} response The raw Connect response object
28413      */
28414     this.addEvents({
28415         'beforerender' : true,
28416         'load' : true,
28417         'loadexception' : true
28418     });
28419 };
28420 Roo.extend(Roo.JsonView, Roo.View, {
28421     /**
28422      * @type {String} The root property in the loaded JSON object that contains the data
28423      */
28424     jsonRoot : "",
28425
28426     /**
28427      * Refreshes the view.
28428      */
28429     refresh : function(){
28430         this.clearSelections();
28431         this.el.update("");
28432         var html = [];
28433         var o = this.jsonData;
28434         if(o && o.length > 0){
28435             for(var i = 0, len = o.length; i < len; i++){
28436                 var data = this.prepareData(o[i], i, o);
28437                 html[html.length] = this.tpl.apply(data);
28438             }
28439         }else{
28440             html.push(this.emptyText);
28441         }
28442         this.el.update(html.join(""));
28443         this.nodes = this.el.dom.childNodes;
28444         this.updateIndexes(0);
28445     },
28446
28447     /**
28448      * 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.
28449      * @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:
28450      <pre><code>
28451      view.load({
28452          url: "your-url.php",
28453          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28454          callback: yourFunction,
28455          scope: yourObject, //(optional scope)
28456          discardUrl: false,
28457          nocache: false,
28458          text: "Loading...",
28459          timeout: 30,
28460          scripts: false
28461      });
28462      </code></pre>
28463      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28464      * 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.
28465      * @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}
28466      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28467      * @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.
28468      */
28469     load : function(){
28470         var um = this.el.getUpdateManager();
28471         um.update.apply(um, arguments);
28472     },
28473
28474     // note - render is a standard framework call...
28475     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28476     render : function(el, response){
28477         
28478         this.clearSelections();
28479         this.el.update("");
28480         var o;
28481         try{
28482             if (response != '') {
28483                 o = Roo.util.JSON.decode(response.responseText);
28484                 if(this.jsonRoot){
28485                     
28486                     o = o[this.jsonRoot];
28487                 }
28488             }
28489         } catch(e){
28490         }
28491         /**
28492          * The current JSON data or null
28493          */
28494         this.jsonData = o;
28495         this.beforeRender();
28496         this.refresh();
28497     },
28498
28499 /**
28500  * Get the number of records in the current JSON dataset
28501  * @return {Number}
28502  */
28503     getCount : function(){
28504         return this.jsonData ? this.jsonData.length : 0;
28505     },
28506
28507 /**
28508  * Returns the JSON object for the specified node(s)
28509  * @param {HTMLElement/Array} node The node or an array of nodes
28510  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28511  * you get the JSON object for the node
28512  */
28513     getNodeData : function(node){
28514         if(node instanceof Array){
28515             var data = [];
28516             for(var i = 0, len = node.length; i < len; i++){
28517                 data.push(this.getNodeData(node[i]));
28518             }
28519             return data;
28520         }
28521         return this.jsonData[this.indexOf(node)] || null;
28522     },
28523
28524     beforeRender : function(){
28525         this.snapshot = this.jsonData;
28526         if(this.sortInfo){
28527             this.sort.apply(this, this.sortInfo);
28528         }
28529         this.fireEvent("beforerender", this, this.jsonData);
28530     },
28531
28532     onLoad : function(el, o){
28533         this.fireEvent("load", this, this.jsonData, o);
28534     },
28535
28536     onLoadException : function(el, o){
28537         this.fireEvent("loadexception", this, o);
28538     },
28539
28540 /**
28541  * Filter the data by a specific property.
28542  * @param {String} property A property on your JSON objects
28543  * @param {String/RegExp} value Either string that the property values
28544  * should start with, or a RegExp to test against the property
28545  */
28546     filter : function(property, value){
28547         if(this.jsonData){
28548             var data = [];
28549             var ss = this.snapshot;
28550             if(typeof value == "string"){
28551                 var vlen = value.length;
28552                 if(vlen == 0){
28553                     this.clearFilter();
28554                     return;
28555                 }
28556                 value = value.toLowerCase();
28557                 for(var i = 0, len = ss.length; i < len; i++){
28558                     var o = ss[i];
28559                     if(o[property].substr(0, vlen).toLowerCase() == value){
28560                         data.push(o);
28561                     }
28562                 }
28563             } else if(value.exec){ // regex?
28564                 for(var i = 0, len = ss.length; i < len; i++){
28565                     var o = ss[i];
28566                     if(value.test(o[property])){
28567                         data.push(o);
28568                     }
28569                 }
28570             } else{
28571                 return;
28572             }
28573             this.jsonData = data;
28574             this.refresh();
28575         }
28576     },
28577
28578 /**
28579  * Filter by a function. The passed function will be called with each
28580  * object in the current dataset. If the function returns true the value is kept,
28581  * otherwise it is filtered.
28582  * @param {Function} fn
28583  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28584  */
28585     filterBy : function(fn, scope){
28586         if(this.jsonData){
28587             var data = [];
28588             var ss = this.snapshot;
28589             for(var i = 0, len = ss.length; i < len; i++){
28590                 var o = ss[i];
28591                 if(fn.call(scope || this, o)){
28592                     data.push(o);
28593                 }
28594             }
28595             this.jsonData = data;
28596             this.refresh();
28597         }
28598     },
28599
28600 /**
28601  * Clears the current filter.
28602  */
28603     clearFilter : function(){
28604         if(this.snapshot && this.jsonData != this.snapshot){
28605             this.jsonData = this.snapshot;
28606             this.refresh();
28607         }
28608     },
28609
28610
28611 /**
28612  * Sorts the data for this view and refreshes it.
28613  * @param {String} property A property on your JSON objects to sort on
28614  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28615  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28616  */
28617     sort : function(property, dir, sortType){
28618         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28619         if(this.jsonData){
28620             var p = property;
28621             var dsc = dir && dir.toLowerCase() == "desc";
28622             var f = function(o1, o2){
28623                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28624                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28625                 ;
28626                 if(v1 < v2){
28627                     return dsc ? +1 : -1;
28628                 } else if(v1 > v2){
28629                     return dsc ? -1 : +1;
28630                 } else{
28631                     return 0;
28632                 }
28633             };
28634             this.jsonData.sort(f);
28635             this.refresh();
28636             if(this.jsonData != this.snapshot){
28637                 this.snapshot.sort(f);
28638             }
28639         }
28640     }
28641 });/*
28642  * Based on:
28643  * Ext JS Library 1.1.1
28644  * Copyright(c) 2006-2007, Ext JS, LLC.
28645  *
28646  * Originally Released Under LGPL - original licence link has changed is not relivant.
28647  *
28648  * Fork - LGPL
28649  * <script type="text/javascript">
28650  */
28651  
28652
28653 /**
28654  * @class Roo.ColorPalette
28655  * @extends Roo.Component
28656  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28657  * Here's an example of typical usage:
28658  * <pre><code>
28659 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28660 cp.render('my-div');
28661
28662 cp.on('select', function(palette, selColor){
28663     // do something with selColor
28664 });
28665 </code></pre>
28666  * @constructor
28667  * Create a new ColorPalette
28668  * @param {Object} config The config object
28669  */
28670 Roo.ColorPalette = function(config){
28671     Roo.ColorPalette.superclass.constructor.call(this, config);
28672     this.addEvents({
28673         /**
28674              * @event select
28675              * Fires when a color is selected
28676              * @param {ColorPalette} this
28677              * @param {String} color The 6-digit color hex code (without the # symbol)
28678              */
28679         select: true
28680     });
28681
28682     if(this.handler){
28683         this.on("select", this.handler, this.scope, true);
28684     }
28685 };
28686 Roo.extend(Roo.ColorPalette, Roo.Component, {
28687     /**
28688      * @cfg {String} itemCls
28689      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28690      */
28691     itemCls : "x-color-palette",
28692     /**
28693      * @cfg {String} value
28694      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28695      * the hex codes are case-sensitive.
28696      */
28697     value : null,
28698     clickEvent:'click',
28699     // private
28700     ctype: "Roo.ColorPalette",
28701
28702     /**
28703      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28704      */
28705     allowReselect : false,
28706
28707     /**
28708      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28709      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28710      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28711      * of colors with the width setting until the box is symmetrical.</p>
28712      * <p>You can override individual colors if needed:</p>
28713      * <pre><code>
28714 var cp = new Roo.ColorPalette();
28715 cp.colors[0] = "FF0000";  // change the first box to red
28716 </code></pre>
28717
28718 Or you can provide a custom array of your own for complete control:
28719 <pre><code>
28720 var cp = new Roo.ColorPalette();
28721 cp.colors = ["000000", "993300", "333300"];
28722 </code></pre>
28723      * @type Array
28724      */
28725     colors : [
28726         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28727         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28728         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28729         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28730         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28731     ],
28732
28733     // private
28734     onRender : function(container, position){
28735         var t = new Roo.MasterTemplate(
28736             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28737         );
28738         var c = this.colors;
28739         for(var i = 0, len = c.length; i < len; i++){
28740             t.add([c[i]]);
28741         }
28742         var el = document.createElement("div");
28743         el.className = this.itemCls;
28744         t.overwrite(el);
28745         container.dom.insertBefore(el, position);
28746         this.el = Roo.get(el);
28747         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28748         if(this.clickEvent != 'click'){
28749             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28750         }
28751     },
28752
28753     // private
28754     afterRender : function(){
28755         Roo.ColorPalette.superclass.afterRender.call(this);
28756         if(this.value){
28757             var s = this.value;
28758             this.value = null;
28759             this.select(s);
28760         }
28761     },
28762
28763     // private
28764     handleClick : function(e, t){
28765         e.preventDefault();
28766         if(!this.disabled){
28767             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28768             this.select(c.toUpperCase());
28769         }
28770     },
28771
28772     /**
28773      * Selects the specified color in the palette (fires the select event)
28774      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28775      */
28776     select : function(color){
28777         color = color.replace("#", "");
28778         if(color != this.value || this.allowReselect){
28779             var el = this.el;
28780             if(this.value){
28781                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28782             }
28783             el.child("a.color-"+color).addClass("x-color-palette-sel");
28784             this.value = color;
28785             this.fireEvent("select", this, color);
28786         }
28787     }
28788 });/*
28789  * Based on:
28790  * Ext JS Library 1.1.1
28791  * Copyright(c) 2006-2007, Ext JS, LLC.
28792  *
28793  * Originally Released Under LGPL - original licence link has changed is not relivant.
28794  *
28795  * Fork - LGPL
28796  * <script type="text/javascript">
28797  */
28798  
28799 /**
28800  * @class Roo.DatePicker
28801  * @extends Roo.Component
28802  * Simple date picker class.
28803  * @constructor
28804  * Create a new DatePicker
28805  * @param {Object} config The config object
28806  */
28807 Roo.DatePicker = function(config){
28808     Roo.DatePicker.superclass.constructor.call(this, config);
28809
28810     this.value = config && config.value ?
28811                  config.value.clearTime() : new Date().clearTime();
28812
28813     this.addEvents({
28814         /**
28815              * @event select
28816              * Fires when a date is selected
28817              * @param {DatePicker} this
28818              * @param {Date} date The selected date
28819              */
28820         'select': true,
28821         /**
28822              * @event monthchange
28823              * Fires when the displayed month changes 
28824              * @param {DatePicker} this
28825              * @param {Date} date The selected month
28826              */
28827         'monthchange': true
28828     });
28829
28830     if(this.handler){
28831         this.on("select", this.handler,  this.scope || this);
28832     }
28833     // build the disabledDatesRE
28834     if(!this.disabledDatesRE && this.disabledDates){
28835         var dd = this.disabledDates;
28836         var re = "(?:";
28837         for(var i = 0; i < dd.length; i++){
28838             re += dd[i];
28839             if(i != dd.length-1) {
28840                 re += "|";
28841             }
28842         }
28843         this.disabledDatesRE = new RegExp(re + ")");
28844     }
28845 };
28846
28847 Roo.extend(Roo.DatePicker, Roo.Component, {
28848     /**
28849      * @cfg {String} todayText
28850      * The text to display on the button that selects the current date (defaults to "Today")
28851      */
28852     todayText : "Today",
28853     /**
28854      * @cfg {String} okText
28855      * The text to display on the ok button
28856      */
28857     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28858     /**
28859      * @cfg {String} cancelText
28860      * The text to display on the cancel button
28861      */
28862     cancelText : "Cancel",
28863     /**
28864      * @cfg {String} todayTip
28865      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28866      */
28867     todayTip : "{0} (Spacebar)",
28868     /**
28869      * @cfg {Date} minDate
28870      * Minimum allowable date (JavaScript date object, defaults to null)
28871      */
28872     minDate : null,
28873     /**
28874      * @cfg {Date} maxDate
28875      * Maximum allowable date (JavaScript date object, defaults to null)
28876      */
28877     maxDate : null,
28878     /**
28879      * @cfg {String} minText
28880      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28881      */
28882     minText : "This date is before the minimum date",
28883     /**
28884      * @cfg {String} maxText
28885      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28886      */
28887     maxText : "This date is after the maximum date",
28888     /**
28889      * @cfg {String} format
28890      * The default date format string which can be overriden for localization support.  The format must be
28891      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28892      */
28893     format : "m/d/y",
28894     /**
28895      * @cfg {Array} disabledDays
28896      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28897      */
28898     disabledDays : null,
28899     /**
28900      * @cfg {String} disabledDaysText
28901      * The tooltip to display when the date falls on a disabled day (defaults to "")
28902      */
28903     disabledDaysText : "",
28904     /**
28905      * @cfg {RegExp} disabledDatesRE
28906      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28907      */
28908     disabledDatesRE : null,
28909     /**
28910      * @cfg {String} disabledDatesText
28911      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28912      */
28913     disabledDatesText : "",
28914     /**
28915      * @cfg {Boolean} constrainToViewport
28916      * True to constrain the date picker to the viewport (defaults to true)
28917      */
28918     constrainToViewport : true,
28919     /**
28920      * @cfg {Array} monthNames
28921      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28922      */
28923     monthNames : Date.monthNames,
28924     /**
28925      * @cfg {Array} dayNames
28926      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28927      */
28928     dayNames : Date.dayNames,
28929     /**
28930      * @cfg {String} nextText
28931      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28932      */
28933     nextText: 'Next Month (Control+Right)',
28934     /**
28935      * @cfg {String} prevText
28936      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28937      */
28938     prevText: 'Previous Month (Control+Left)',
28939     /**
28940      * @cfg {String} monthYearText
28941      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28942      */
28943     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28944     /**
28945      * @cfg {Number} startDay
28946      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28947      */
28948     startDay : 0,
28949     /**
28950      * @cfg {Bool} showClear
28951      * Show a clear button (usefull for date form elements that can be blank.)
28952      */
28953     
28954     showClear: false,
28955     
28956     /**
28957      * Sets the value of the date field
28958      * @param {Date} value The date to set
28959      */
28960     setValue : function(value){
28961         var old = this.value;
28962         
28963         if (typeof(value) == 'string') {
28964          
28965             value = Date.parseDate(value, this.format);
28966         }
28967         if (!value) {
28968             value = new Date();
28969         }
28970         
28971         this.value = value.clearTime(true);
28972         if(this.el){
28973             this.update(this.value);
28974         }
28975     },
28976
28977     /**
28978      * Gets the current selected value of the date field
28979      * @return {Date} The selected date
28980      */
28981     getValue : function(){
28982         return this.value;
28983     },
28984
28985     // private
28986     focus : function(){
28987         if(this.el){
28988             this.update(this.activeDate);
28989         }
28990     },
28991
28992     // privateval
28993     onRender : function(container, position){
28994         
28995         var m = [
28996              '<table cellspacing="0">',
28997                 '<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>',
28998                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28999         var dn = this.dayNames;
29000         for(var i = 0; i < 7; i++){
29001             var d = this.startDay+i;
29002             if(d > 6){
29003                 d = d-7;
29004             }
29005             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29006         }
29007         m[m.length] = "</tr></thead><tbody><tr>";
29008         for(var i = 0; i < 42; i++) {
29009             if(i % 7 == 0 && i != 0){
29010                 m[m.length] = "</tr><tr>";
29011             }
29012             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29013         }
29014         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29015             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29016
29017         var el = document.createElement("div");
29018         el.className = "x-date-picker";
29019         el.innerHTML = m.join("");
29020
29021         container.dom.insertBefore(el, position);
29022
29023         this.el = Roo.get(el);
29024         this.eventEl = Roo.get(el.firstChild);
29025
29026         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29027             handler: this.showPrevMonth,
29028             scope: this,
29029             preventDefault:true,
29030             stopDefault:true
29031         });
29032
29033         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29034             handler: this.showNextMonth,
29035             scope: this,
29036             preventDefault:true,
29037             stopDefault:true
29038         });
29039
29040         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29041
29042         this.monthPicker = this.el.down('div.x-date-mp');
29043         this.monthPicker.enableDisplayMode('block');
29044         
29045         var kn = new Roo.KeyNav(this.eventEl, {
29046             "left" : function(e){
29047                 e.ctrlKey ?
29048                     this.showPrevMonth() :
29049                     this.update(this.activeDate.add("d", -1));
29050             },
29051
29052             "right" : function(e){
29053                 e.ctrlKey ?
29054                     this.showNextMonth() :
29055                     this.update(this.activeDate.add("d", 1));
29056             },
29057
29058             "up" : function(e){
29059                 e.ctrlKey ?
29060                     this.showNextYear() :
29061                     this.update(this.activeDate.add("d", -7));
29062             },
29063
29064             "down" : function(e){
29065                 e.ctrlKey ?
29066                     this.showPrevYear() :
29067                     this.update(this.activeDate.add("d", 7));
29068             },
29069
29070             "pageUp" : function(e){
29071                 this.showNextMonth();
29072             },
29073
29074             "pageDown" : function(e){
29075                 this.showPrevMonth();
29076             },
29077
29078             "enter" : function(e){
29079                 e.stopPropagation();
29080                 return true;
29081             },
29082
29083             scope : this
29084         });
29085
29086         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29087
29088         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29089
29090         this.el.unselectable();
29091         
29092         this.cells = this.el.select("table.x-date-inner tbody td");
29093         this.textNodes = this.el.query("table.x-date-inner tbody span");
29094
29095         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29096             text: "&#160;",
29097             tooltip: this.monthYearText
29098         });
29099
29100         this.mbtn.on('click', this.showMonthPicker, this);
29101         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29102
29103
29104         var today = (new Date()).dateFormat(this.format);
29105         
29106         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29107         if (this.showClear) {
29108             baseTb.add( new Roo.Toolbar.Fill());
29109         }
29110         baseTb.add({
29111             text: String.format(this.todayText, today),
29112             tooltip: String.format(this.todayTip, today),
29113             handler: this.selectToday,
29114             scope: this
29115         });
29116         
29117         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29118             
29119         //});
29120         if (this.showClear) {
29121             
29122             baseTb.add( new Roo.Toolbar.Fill());
29123             baseTb.add({
29124                 text: '&#160;',
29125                 cls: 'x-btn-icon x-btn-clear',
29126                 handler: function() {
29127                     //this.value = '';
29128                     this.fireEvent("select", this, '');
29129                 },
29130                 scope: this
29131             });
29132         }
29133         
29134         
29135         if(Roo.isIE){
29136             this.el.repaint();
29137         }
29138         this.update(this.value);
29139     },
29140
29141     createMonthPicker : function(){
29142         if(!this.monthPicker.dom.firstChild){
29143             var buf = ['<table border="0" cellspacing="0">'];
29144             for(var i = 0; i < 6; i++){
29145                 buf.push(
29146                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29147                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29148                     i == 0 ?
29149                     '<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>' :
29150                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29151                 );
29152             }
29153             buf.push(
29154                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29155                     this.okText,
29156                     '</button><button type="button" class="x-date-mp-cancel">',
29157                     this.cancelText,
29158                     '</button></td></tr>',
29159                 '</table>'
29160             );
29161             this.monthPicker.update(buf.join(''));
29162             this.monthPicker.on('click', this.onMonthClick, this);
29163             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29164
29165             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29166             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29167
29168             this.mpMonths.each(function(m, a, i){
29169                 i += 1;
29170                 if((i%2) == 0){
29171                     m.dom.xmonth = 5 + Math.round(i * .5);
29172                 }else{
29173                     m.dom.xmonth = Math.round((i-1) * .5);
29174                 }
29175             });
29176         }
29177     },
29178
29179     showMonthPicker : function(){
29180         this.createMonthPicker();
29181         var size = this.el.getSize();
29182         this.monthPicker.setSize(size);
29183         this.monthPicker.child('table').setSize(size);
29184
29185         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29186         this.updateMPMonth(this.mpSelMonth);
29187         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29188         this.updateMPYear(this.mpSelYear);
29189
29190         this.monthPicker.slideIn('t', {duration:.2});
29191     },
29192
29193     updateMPYear : function(y){
29194         this.mpyear = y;
29195         var ys = this.mpYears.elements;
29196         for(var i = 1; i <= 10; i++){
29197             var td = ys[i-1], y2;
29198             if((i%2) == 0){
29199                 y2 = y + Math.round(i * .5);
29200                 td.firstChild.innerHTML = y2;
29201                 td.xyear = y2;
29202             }else{
29203                 y2 = y - (5-Math.round(i * .5));
29204                 td.firstChild.innerHTML = y2;
29205                 td.xyear = y2;
29206             }
29207             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29208         }
29209     },
29210
29211     updateMPMonth : function(sm){
29212         this.mpMonths.each(function(m, a, i){
29213             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29214         });
29215     },
29216
29217     selectMPMonth: function(m){
29218         
29219     },
29220
29221     onMonthClick : function(e, t){
29222         e.stopEvent();
29223         var el = new Roo.Element(t), pn;
29224         if(el.is('button.x-date-mp-cancel')){
29225             this.hideMonthPicker();
29226         }
29227         else if(el.is('button.x-date-mp-ok')){
29228             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29229             this.hideMonthPicker();
29230         }
29231         else if(pn = el.up('td.x-date-mp-month', 2)){
29232             this.mpMonths.removeClass('x-date-mp-sel');
29233             pn.addClass('x-date-mp-sel');
29234             this.mpSelMonth = pn.dom.xmonth;
29235         }
29236         else if(pn = el.up('td.x-date-mp-year', 2)){
29237             this.mpYears.removeClass('x-date-mp-sel');
29238             pn.addClass('x-date-mp-sel');
29239             this.mpSelYear = pn.dom.xyear;
29240         }
29241         else if(el.is('a.x-date-mp-prev')){
29242             this.updateMPYear(this.mpyear-10);
29243         }
29244         else if(el.is('a.x-date-mp-next')){
29245             this.updateMPYear(this.mpyear+10);
29246         }
29247     },
29248
29249     onMonthDblClick : function(e, t){
29250         e.stopEvent();
29251         var el = new Roo.Element(t), pn;
29252         if(pn = el.up('td.x-date-mp-month', 2)){
29253             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29254             this.hideMonthPicker();
29255         }
29256         else if(pn = el.up('td.x-date-mp-year', 2)){
29257             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29258             this.hideMonthPicker();
29259         }
29260     },
29261
29262     hideMonthPicker : function(disableAnim){
29263         if(this.monthPicker){
29264             if(disableAnim === true){
29265                 this.monthPicker.hide();
29266             }else{
29267                 this.monthPicker.slideOut('t', {duration:.2});
29268             }
29269         }
29270     },
29271
29272     // private
29273     showPrevMonth : function(e){
29274         this.update(this.activeDate.add("mo", -1));
29275     },
29276
29277     // private
29278     showNextMonth : function(e){
29279         this.update(this.activeDate.add("mo", 1));
29280     },
29281
29282     // private
29283     showPrevYear : function(){
29284         this.update(this.activeDate.add("y", -1));
29285     },
29286
29287     // private
29288     showNextYear : function(){
29289         this.update(this.activeDate.add("y", 1));
29290     },
29291
29292     // private
29293     handleMouseWheel : function(e){
29294         var delta = e.getWheelDelta();
29295         if(delta > 0){
29296             this.showPrevMonth();
29297             e.stopEvent();
29298         } else if(delta < 0){
29299             this.showNextMonth();
29300             e.stopEvent();
29301         }
29302     },
29303
29304     // private
29305     handleDateClick : function(e, t){
29306         e.stopEvent();
29307         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29308             this.setValue(new Date(t.dateValue));
29309             this.fireEvent("select", this, this.value);
29310         }
29311     },
29312
29313     // private
29314     selectToday : function(){
29315         this.setValue(new Date().clearTime());
29316         this.fireEvent("select", this, this.value);
29317     },
29318
29319     // private
29320     update : function(date)
29321     {
29322         var vd = this.activeDate;
29323         this.activeDate = date;
29324         if(vd && this.el){
29325             var t = date.getTime();
29326             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29327                 this.cells.removeClass("x-date-selected");
29328                 this.cells.each(function(c){
29329                    if(c.dom.firstChild.dateValue == t){
29330                        c.addClass("x-date-selected");
29331                        setTimeout(function(){
29332                             try{c.dom.firstChild.focus();}catch(e){}
29333                        }, 50);
29334                        return false;
29335                    }
29336                 });
29337                 return;
29338             }
29339         }
29340         
29341         var days = date.getDaysInMonth();
29342         var firstOfMonth = date.getFirstDateOfMonth();
29343         var startingPos = firstOfMonth.getDay()-this.startDay;
29344
29345         if(startingPos <= this.startDay){
29346             startingPos += 7;
29347         }
29348
29349         var pm = date.add("mo", -1);
29350         var prevStart = pm.getDaysInMonth()-startingPos;
29351
29352         var cells = this.cells.elements;
29353         var textEls = this.textNodes;
29354         days += startingPos;
29355
29356         // convert everything to numbers so it's fast
29357         var day = 86400000;
29358         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29359         var today = new Date().clearTime().getTime();
29360         var sel = date.clearTime().getTime();
29361         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29362         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29363         var ddMatch = this.disabledDatesRE;
29364         var ddText = this.disabledDatesText;
29365         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29366         var ddaysText = this.disabledDaysText;
29367         var format = this.format;
29368
29369         var setCellClass = function(cal, cell){
29370             cell.title = "";
29371             var t = d.getTime();
29372             cell.firstChild.dateValue = t;
29373             if(t == today){
29374                 cell.className += " x-date-today";
29375                 cell.title = cal.todayText;
29376             }
29377             if(t == sel){
29378                 cell.className += " x-date-selected";
29379                 setTimeout(function(){
29380                     try{cell.firstChild.focus();}catch(e){}
29381                 }, 50);
29382             }
29383             // disabling
29384             if(t < min) {
29385                 cell.className = " x-date-disabled";
29386                 cell.title = cal.minText;
29387                 return;
29388             }
29389             if(t > max) {
29390                 cell.className = " x-date-disabled";
29391                 cell.title = cal.maxText;
29392                 return;
29393             }
29394             if(ddays){
29395                 if(ddays.indexOf(d.getDay()) != -1){
29396                     cell.title = ddaysText;
29397                     cell.className = " x-date-disabled";
29398                 }
29399             }
29400             if(ddMatch && format){
29401                 var fvalue = d.dateFormat(format);
29402                 if(ddMatch.test(fvalue)){
29403                     cell.title = ddText.replace("%0", fvalue);
29404                     cell.className = " x-date-disabled";
29405                 }
29406             }
29407         };
29408
29409         var i = 0;
29410         for(; i < startingPos; i++) {
29411             textEls[i].innerHTML = (++prevStart);
29412             d.setDate(d.getDate()+1);
29413             cells[i].className = "x-date-prevday";
29414             setCellClass(this, cells[i]);
29415         }
29416         for(; i < days; i++){
29417             intDay = i - startingPos + 1;
29418             textEls[i].innerHTML = (intDay);
29419             d.setDate(d.getDate()+1);
29420             cells[i].className = "x-date-active";
29421             setCellClass(this, cells[i]);
29422         }
29423         var extraDays = 0;
29424         for(; i < 42; i++) {
29425              textEls[i].innerHTML = (++extraDays);
29426              d.setDate(d.getDate()+1);
29427              cells[i].className = "x-date-nextday";
29428              setCellClass(this, cells[i]);
29429         }
29430
29431         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29432         this.fireEvent('monthchange', this, date);
29433         
29434         if(!this.internalRender){
29435             var main = this.el.dom.firstChild;
29436             var w = main.offsetWidth;
29437             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29438             Roo.fly(main).setWidth(w);
29439             this.internalRender = true;
29440             // opera does not respect the auto grow header center column
29441             // then, after it gets a width opera refuses to recalculate
29442             // without a second pass
29443             if(Roo.isOpera && !this.secondPass){
29444                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29445                 this.secondPass = true;
29446                 this.update.defer(10, this, [date]);
29447             }
29448         }
29449         
29450         
29451     }
29452 });        /*
29453  * Based on:
29454  * Ext JS Library 1.1.1
29455  * Copyright(c) 2006-2007, Ext JS, LLC.
29456  *
29457  * Originally Released Under LGPL - original licence link has changed is not relivant.
29458  *
29459  * Fork - LGPL
29460  * <script type="text/javascript">
29461  */
29462 /**
29463  * @class Roo.TabPanel
29464  * @extends Roo.util.Observable
29465  * A lightweight tab container.
29466  * <br><br>
29467  * Usage:
29468  * <pre><code>
29469 // basic tabs 1, built from existing content
29470 var tabs = new Roo.TabPanel("tabs1");
29471 tabs.addTab("script", "View Script");
29472 tabs.addTab("markup", "View Markup");
29473 tabs.activate("script");
29474
29475 // more advanced tabs, built from javascript
29476 var jtabs = new Roo.TabPanel("jtabs");
29477 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29478
29479 // set up the UpdateManager
29480 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29481 var updater = tab2.getUpdateManager();
29482 updater.setDefaultUrl("ajax1.htm");
29483 tab2.on('activate', updater.refresh, updater, true);
29484
29485 // Use setUrl for Ajax loading
29486 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29487 tab3.setUrl("ajax2.htm", null, true);
29488
29489 // Disabled tab
29490 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29491 tab4.disable();
29492
29493 jtabs.activate("jtabs-1");
29494  * </code></pre>
29495  * @constructor
29496  * Create a new TabPanel.
29497  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29498  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29499  */
29500 Roo.TabPanel = function(container, config){
29501     /**
29502     * The container element for this TabPanel.
29503     * @type Roo.Element
29504     */
29505     this.el = Roo.get(container, true);
29506     if(config){
29507         if(typeof config == "boolean"){
29508             this.tabPosition = config ? "bottom" : "top";
29509         }else{
29510             Roo.apply(this, config);
29511         }
29512     }
29513     if(this.tabPosition == "bottom"){
29514         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29515         this.el.addClass("x-tabs-bottom");
29516     }
29517     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29518     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29519     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29520     if(Roo.isIE){
29521         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29522     }
29523     if(this.tabPosition != "bottom"){
29524         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29525          * @type Roo.Element
29526          */
29527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29528         this.el.addClass("x-tabs-top");
29529     }
29530     this.items = [];
29531
29532     this.bodyEl.setStyle("position", "relative");
29533
29534     this.active = null;
29535     this.activateDelegate = this.activate.createDelegate(this);
29536
29537     this.addEvents({
29538         /**
29539          * @event tabchange
29540          * Fires when the active tab changes
29541          * @param {Roo.TabPanel} this
29542          * @param {Roo.TabPanelItem} activePanel The new active tab
29543          */
29544         "tabchange": true,
29545         /**
29546          * @event beforetabchange
29547          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29548          * @param {Roo.TabPanel} this
29549          * @param {Object} e Set cancel to true on this object to cancel the tab change
29550          * @param {Roo.TabPanelItem} tab The tab being changed to
29551          */
29552         "beforetabchange" : true
29553     });
29554
29555     Roo.EventManager.onWindowResize(this.onResize, this);
29556     this.cpad = this.el.getPadding("lr");
29557     this.hiddenCount = 0;
29558
29559
29560     // toolbar on the tabbar support...
29561     if (this.toolbar) {
29562         var tcfg = this.toolbar;
29563         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29564         this.toolbar = new Roo.Toolbar(tcfg);
29565         if (Roo.isSafari) {
29566             var tbl = tcfg.container.child('table', true);
29567             tbl.setAttribute('width', '100%');
29568         }
29569         
29570     }
29571    
29572
29573
29574     Roo.TabPanel.superclass.constructor.call(this);
29575 };
29576
29577 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29578     /*
29579      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29580      */
29581     tabPosition : "top",
29582     /*
29583      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29584      */
29585     currentTabWidth : 0,
29586     /*
29587      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29588      */
29589     minTabWidth : 40,
29590     /*
29591      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29592      */
29593     maxTabWidth : 250,
29594     /*
29595      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29596      */
29597     preferredTabWidth : 175,
29598     /*
29599      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29600      */
29601     resizeTabs : false,
29602     /*
29603      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29604      */
29605     monitorResize : true,
29606     /*
29607      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29608      */
29609     toolbar : false,
29610
29611     /**
29612      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29613      * @param {String} id The id of the div to use <b>or create</b>
29614      * @param {String} text The text for the tab
29615      * @param {String} content (optional) Content to put in the TabPanelItem body
29616      * @param {Boolean} closable (optional) True to create a close icon on the tab
29617      * @return {Roo.TabPanelItem} The created TabPanelItem
29618      */
29619     addTab : function(id, text, content, closable){
29620         var item = new Roo.TabPanelItem(this, id, text, closable);
29621         this.addTabItem(item);
29622         if(content){
29623             item.setContent(content);
29624         }
29625         return item;
29626     },
29627
29628     /**
29629      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29630      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29631      * @return {Roo.TabPanelItem}
29632      */
29633     getTab : function(id){
29634         return this.items[id];
29635     },
29636
29637     /**
29638      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29639      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29640      */
29641     hideTab : function(id){
29642         var t = this.items[id];
29643         if(!t.isHidden()){
29644            t.setHidden(true);
29645            this.hiddenCount++;
29646            this.autoSizeTabs();
29647         }
29648     },
29649
29650     /**
29651      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29652      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29653      */
29654     unhideTab : function(id){
29655         var t = this.items[id];
29656         if(t.isHidden()){
29657            t.setHidden(false);
29658            this.hiddenCount--;
29659            this.autoSizeTabs();
29660         }
29661     },
29662
29663     /**
29664      * Adds an existing {@link Roo.TabPanelItem}.
29665      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29666      */
29667     addTabItem : function(item){
29668         this.items[item.id] = item;
29669         this.items.push(item);
29670         if(this.resizeTabs){
29671            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29672            this.autoSizeTabs();
29673         }else{
29674             item.autoSize();
29675         }
29676     },
29677
29678     /**
29679      * Removes a {@link Roo.TabPanelItem}.
29680      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29681      */
29682     removeTab : function(id){
29683         var items = this.items;
29684         var tab = items[id];
29685         if(!tab) { return; }
29686         var index = items.indexOf(tab);
29687         if(this.active == tab && items.length > 1){
29688             var newTab = this.getNextAvailable(index);
29689             if(newTab) {
29690                 newTab.activate();
29691             }
29692         }
29693         this.stripEl.dom.removeChild(tab.pnode.dom);
29694         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29695             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29696         }
29697         items.splice(index, 1);
29698         delete this.items[tab.id];
29699         tab.fireEvent("close", tab);
29700         tab.purgeListeners();
29701         this.autoSizeTabs();
29702     },
29703
29704     getNextAvailable : function(start){
29705         var items = this.items;
29706         var index = start;
29707         // look for a next tab that will slide over to
29708         // replace the one being removed
29709         while(index < items.length){
29710             var item = items[++index];
29711             if(item && !item.isHidden()){
29712                 return item;
29713             }
29714         }
29715         // if one isn't found select the previous tab (on the left)
29716         index = start;
29717         while(index >= 0){
29718             var item = items[--index];
29719             if(item && !item.isHidden()){
29720                 return item;
29721             }
29722         }
29723         return null;
29724     },
29725
29726     /**
29727      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29728      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29729      */
29730     disableTab : function(id){
29731         var tab = this.items[id];
29732         if(tab && this.active != tab){
29733             tab.disable();
29734         }
29735     },
29736
29737     /**
29738      * Enables a {@link Roo.TabPanelItem} that is disabled.
29739      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29740      */
29741     enableTab : function(id){
29742         var tab = this.items[id];
29743         tab.enable();
29744     },
29745
29746     /**
29747      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29748      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29749      * @return {Roo.TabPanelItem} The TabPanelItem.
29750      */
29751     activate : function(id){
29752         var tab = this.items[id];
29753         if(!tab){
29754             return null;
29755         }
29756         if(tab == this.active || tab.disabled){
29757             return tab;
29758         }
29759         var e = {};
29760         this.fireEvent("beforetabchange", this, e, tab);
29761         if(e.cancel !== true && !tab.disabled){
29762             if(this.active){
29763                 this.active.hide();
29764             }
29765             this.active = this.items[id];
29766             this.active.show();
29767             this.fireEvent("tabchange", this, this.active);
29768         }
29769         return tab;
29770     },
29771
29772     /**
29773      * Gets the active {@link Roo.TabPanelItem}.
29774      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29775      */
29776     getActiveTab : function(){
29777         return this.active;
29778     },
29779
29780     /**
29781      * Updates the tab body element to fit the height of the container element
29782      * for overflow scrolling
29783      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29784      */
29785     syncHeight : function(targetHeight){
29786         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29787         var bm = this.bodyEl.getMargins();
29788         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29789         this.bodyEl.setHeight(newHeight);
29790         return newHeight;
29791     },
29792
29793     onResize : function(){
29794         if(this.monitorResize){
29795             this.autoSizeTabs();
29796         }
29797     },
29798
29799     /**
29800      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29801      */
29802     beginUpdate : function(){
29803         this.updating = true;
29804     },
29805
29806     /**
29807      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29808      */
29809     endUpdate : function(){
29810         this.updating = false;
29811         this.autoSizeTabs();
29812     },
29813
29814     /**
29815      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29816      */
29817     autoSizeTabs : function(){
29818         var count = this.items.length;
29819         var vcount = count - this.hiddenCount;
29820         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29821             return;
29822         }
29823         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29824         var availWidth = Math.floor(w / vcount);
29825         var b = this.stripBody;
29826         if(b.getWidth() > w){
29827             var tabs = this.items;
29828             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29829             if(availWidth < this.minTabWidth){
29830                 /*if(!this.sleft){    // incomplete scrolling code
29831                     this.createScrollButtons();
29832                 }
29833                 this.showScroll();
29834                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29835             }
29836         }else{
29837             if(this.currentTabWidth < this.preferredTabWidth){
29838                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29839             }
29840         }
29841     },
29842
29843     /**
29844      * Returns the number of tabs in this TabPanel.
29845      * @return {Number}
29846      */
29847      getCount : function(){
29848          return this.items.length;
29849      },
29850
29851     /**
29852      * Resizes all the tabs to the passed width
29853      * @param {Number} The new width
29854      */
29855     setTabWidth : function(width){
29856         this.currentTabWidth = width;
29857         for(var i = 0, len = this.items.length; i < len; i++) {
29858                 if(!this.items[i].isHidden()) {
29859                 this.items[i].setWidth(width);
29860             }
29861         }
29862     },
29863
29864     /**
29865      * Destroys this TabPanel
29866      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29867      */
29868     destroy : function(removeEl){
29869         Roo.EventManager.removeResizeListener(this.onResize, this);
29870         for(var i = 0, len = this.items.length; i < len; i++){
29871             this.items[i].purgeListeners();
29872         }
29873         if(removeEl === true){
29874             this.el.update("");
29875             this.el.remove();
29876         }
29877     }
29878 });
29879
29880 /**
29881  * @class Roo.TabPanelItem
29882  * @extends Roo.util.Observable
29883  * Represents an individual item (tab plus body) in a TabPanel.
29884  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29885  * @param {String} id The id of this TabPanelItem
29886  * @param {String} text The text for the tab of this TabPanelItem
29887  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29888  */
29889 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29890     /**
29891      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29892      * @type Roo.TabPanel
29893      */
29894     this.tabPanel = tabPanel;
29895     /**
29896      * The id for this TabPanelItem
29897      * @type String
29898      */
29899     this.id = id;
29900     /** @private */
29901     this.disabled = false;
29902     /** @private */
29903     this.text = text;
29904     /** @private */
29905     this.loaded = false;
29906     this.closable = closable;
29907
29908     /**
29909      * The body element for this TabPanelItem.
29910      * @type Roo.Element
29911      */
29912     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29913     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29914     this.bodyEl.setStyle("display", "block");
29915     this.bodyEl.setStyle("zoom", "1");
29916     this.hideAction();
29917
29918     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29919     /** @private */
29920     this.el = Roo.get(els.el, true);
29921     this.inner = Roo.get(els.inner, true);
29922     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29923     this.pnode = Roo.get(els.el.parentNode, true);
29924     this.el.on("mousedown", this.onTabMouseDown, this);
29925     this.el.on("click", this.onTabClick, this);
29926     /** @private */
29927     if(closable){
29928         var c = Roo.get(els.close, true);
29929         c.dom.title = this.closeText;
29930         c.addClassOnOver("close-over");
29931         c.on("click", this.closeClick, this);
29932      }
29933
29934     this.addEvents({
29935          /**
29936          * @event activate
29937          * Fires when this tab becomes the active tab.
29938          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29939          * @param {Roo.TabPanelItem} this
29940          */
29941         "activate": true,
29942         /**
29943          * @event beforeclose
29944          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29945          * @param {Roo.TabPanelItem} this
29946          * @param {Object} e Set cancel to true on this object to cancel the close.
29947          */
29948         "beforeclose": true,
29949         /**
29950          * @event close
29951          * Fires when this tab is closed.
29952          * @param {Roo.TabPanelItem} this
29953          */
29954          "close": true,
29955         /**
29956          * @event deactivate
29957          * Fires when this tab is no longer the active tab.
29958          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29959          * @param {Roo.TabPanelItem} this
29960          */
29961          "deactivate" : true
29962     });
29963     this.hidden = false;
29964
29965     Roo.TabPanelItem.superclass.constructor.call(this);
29966 };
29967
29968 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29969     purgeListeners : function(){
29970        Roo.util.Observable.prototype.purgeListeners.call(this);
29971        this.el.removeAllListeners();
29972     },
29973     /**
29974      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29975      */
29976     show : function(){
29977         this.pnode.addClass("on");
29978         this.showAction();
29979         if(Roo.isOpera){
29980             this.tabPanel.stripWrap.repaint();
29981         }
29982         this.fireEvent("activate", this.tabPanel, this);
29983     },
29984
29985     /**
29986      * Returns true if this tab is the active tab.
29987      * @return {Boolean}
29988      */
29989     isActive : function(){
29990         return this.tabPanel.getActiveTab() == this;
29991     },
29992
29993     /**
29994      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29995      */
29996     hide : function(){
29997         this.pnode.removeClass("on");
29998         this.hideAction();
29999         this.fireEvent("deactivate", this.tabPanel, this);
30000     },
30001
30002     hideAction : function(){
30003         this.bodyEl.hide();
30004         this.bodyEl.setStyle("position", "absolute");
30005         this.bodyEl.setLeft("-20000px");
30006         this.bodyEl.setTop("-20000px");
30007     },
30008
30009     showAction : function(){
30010         this.bodyEl.setStyle("position", "relative");
30011         this.bodyEl.setTop("");
30012         this.bodyEl.setLeft("");
30013         this.bodyEl.show();
30014     },
30015
30016     /**
30017      * Set the tooltip for the tab.
30018      * @param {String} tooltip The tab's tooltip
30019      */
30020     setTooltip : function(text){
30021         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30022             this.textEl.dom.qtip = text;
30023             this.textEl.dom.removeAttribute('title');
30024         }else{
30025             this.textEl.dom.title = text;
30026         }
30027     },
30028
30029     onTabClick : function(e){
30030         e.preventDefault();
30031         this.tabPanel.activate(this.id);
30032     },
30033
30034     onTabMouseDown : function(e){
30035         e.preventDefault();
30036         this.tabPanel.activate(this.id);
30037     },
30038
30039     getWidth : function(){
30040         return this.inner.getWidth();
30041     },
30042
30043     setWidth : function(width){
30044         var iwidth = width - this.pnode.getPadding("lr");
30045         this.inner.setWidth(iwidth);
30046         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30047         this.pnode.setWidth(width);
30048     },
30049
30050     /**
30051      * Show or hide the tab
30052      * @param {Boolean} hidden True to hide or false to show.
30053      */
30054     setHidden : function(hidden){
30055         this.hidden = hidden;
30056         this.pnode.setStyle("display", hidden ? "none" : "");
30057     },
30058
30059     /**
30060      * Returns true if this tab is "hidden"
30061      * @return {Boolean}
30062      */
30063     isHidden : function(){
30064         return this.hidden;
30065     },
30066
30067     /**
30068      * Returns the text for this tab
30069      * @return {String}
30070      */
30071     getText : function(){
30072         return this.text;
30073     },
30074
30075     autoSize : function(){
30076         //this.el.beginMeasure();
30077         this.textEl.setWidth(1);
30078         /*
30079          *  #2804 [new] Tabs in Roojs
30080          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30081          */
30082         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30083         //this.el.endMeasure();
30084     },
30085
30086     /**
30087      * Sets the text for the tab (Note: this also sets the tooltip text)
30088      * @param {String} text The tab's text and tooltip
30089      */
30090     setText : function(text){
30091         this.text = text;
30092         this.textEl.update(text);
30093         this.setTooltip(text);
30094         if(!this.tabPanel.resizeTabs){
30095             this.autoSize();
30096         }
30097     },
30098     /**
30099      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30100      */
30101     activate : function(){
30102         this.tabPanel.activate(this.id);
30103     },
30104
30105     /**
30106      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30107      */
30108     disable : function(){
30109         if(this.tabPanel.active != this){
30110             this.disabled = true;
30111             this.pnode.addClass("disabled");
30112         }
30113     },
30114
30115     /**
30116      * Enables this TabPanelItem if it was previously disabled.
30117      */
30118     enable : function(){
30119         this.disabled = false;
30120         this.pnode.removeClass("disabled");
30121     },
30122
30123     /**
30124      * Sets the content for this TabPanelItem.
30125      * @param {String} content The content
30126      * @param {Boolean} loadScripts true to look for and load scripts
30127      */
30128     setContent : function(content, loadScripts){
30129         this.bodyEl.update(content, loadScripts);
30130     },
30131
30132     /**
30133      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30134      * @return {Roo.UpdateManager} The UpdateManager
30135      */
30136     getUpdateManager : function(){
30137         return this.bodyEl.getUpdateManager();
30138     },
30139
30140     /**
30141      * Set a URL to be used to load the content for this TabPanelItem.
30142      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30143      * @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)
30144      * @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)
30145      * @return {Roo.UpdateManager} The UpdateManager
30146      */
30147     setUrl : function(url, params, loadOnce){
30148         if(this.refreshDelegate){
30149             this.un('activate', this.refreshDelegate);
30150         }
30151         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30152         this.on("activate", this.refreshDelegate);
30153         return this.bodyEl.getUpdateManager();
30154     },
30155
30156     /** @private */
30157     _handleRefresh : function(url, params, loadOnce){
30158         if(!loadOnce || !this.loaded){
30159             var updater = this.bodyEl.getUpdateManager();
30160             updater.update(url, params, this._setLoaded.createDelegate(this));
30161         }
30162     },
30163
30164     /**
30165      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30166      *   Will fail silently if the setUrl method has not been called.
30167      *   This does not activate the panel, just updates its content.
30168      */
30169     refresh : function(){
30170         if(this.refreshDelegate){
30171            this.loaded = false;
30172            this.refreshDelegate();
30173         }
30174     },
30175
30176     /** @private */
30177     _setLoaded : function(){
30178         this.loaded = true;
30179     },
30180
30181     /** @private */
30182     closeClick : function(e){
30183         var o = {};
30184         e.stopEvent();
30185         this.fireEvent("beforeclose", this, o);
30186         if(o.cancel !== true){
30187             this.tabPanel.removeTab(this.id);
30188         }
30189     },
30190     /**
30191      * The text displayed in the tooltip for the close icon.
30192      * @type String
30193      */
30194     closeText : "Close this tab"
30195 });
30196
30197 /** @private */
30198 Roo.TabPanel.prototype.createStrip = function(container){
30199     var strip = document.createElement("div");
30200     strip.className = "x-tabs-wrap";
30201     container.appendChild(strip);
30202     return strip;
30203 };
30204 /** @private */
30205 Roo.TabPanel.prototype.createStripList = function(strip){
30206     // div wrapper for retard IE
30207     // returns the "tr" element.
30208     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30209         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30210         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30211     return strip.firstChild.firstChild.firstChild.firstChild;
30212 };
30213 /** @private */
30214 Roo.TabPanel.prototype.createBody = function(container){
30215     var body = document.createElement("div");
30216     Roo.id(body, "tab-body");
30217     Roo.fly(body).addClass("x-tabs-body");
30218     container.appendChild(body);
30219     return body;
30220 };
30221 /** @private */
30222 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30223     var body = Roo.getDom(id);
30224     if(!body){
30225         body = document.createElement("div");
30226         body.id = id;
30227     }
30228     Roo.fly(body).addClass("x-tabs-item-body");
30229     bodyEl.insertBefore(body, bodyEl.firstChild);
30230     return body;
30231 };
30232 /** @private */
30233 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30234     var td = document.createElement("td");
30235     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30236     //stripEl.appendChild(td);
30237     if(closable){
30238         td.className = "x-tabs-closable";
30239         if(!this.closeTpl){
30240             this.closeTpl = new Roo.Template(
30241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30243                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30244             );
30245         }
30246         var el = this.closeTpl.overwrite(td, {"text": text});
30247         var close = el.getElementsByTagName("div")[0];
30248         var inner = el.getElementsByTagName("em")[0];
30249         return {"el": el, "close": close, "inner": inner};
30250     } else {
30251         if(!this.tabTpl){
30252             this.tabTpl = new Roo.Template(
30253                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30254                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30255             );
30256         }
30257         var el = this.tabTpl.overwrite(td, {"text": text});
30258         var inner = el.getElementsByTagName("em")[0];
30259         return {"el": el, "inner": inner};
30260     }
30261 };/*
30262  * Based on:
30263  * Ext JS Library 1.1.1
30264  * Copyright(c) 2006-2007, Ext JS, LLC.
30265  *
30266  * Originally Released Under LGPL - original licence link has changed is not relivant.
30267  *
30268  * Fork - LGPL
30269  * <script type="text/javascript">
30270  */
30271
30272 /**
30273  * @class Roo.Button
30274  * @extends Roo.util.Observable
30275  * Simple Button class
30276  * @cfg {String} text The button text
30277  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30278  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30279  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30280  * @cfg {Object} scope The scope of the handler
30281  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30282  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30283  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30284  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30285  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30286  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30287    applies if enableToggle = true)
30288  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30289  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30290   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30291  * @constructor
30292  * Create a new button
30293  * @param {Object} config The config object
30294  */
30295 Roo.Button = function(renderTo, config)
30296 {
30297     if (!config) {
30298         config = renderTo;
30299         renderTo = config.renderTo || false;
30300     }
30301     
30302     Roo.apply(this, config);
30303     this.addEvents({
30304         /**
30305              * @event click
30306              * Fires when this button is clicked
30307              * @param {Button} this
30308              * @param {EventObject} e The click event
30309              */
30310             "click" : true,
30311         /**
30312              * @event toggle
30313              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30314              * @param {Button} this
30315              * @param {Boolean} pressed
30316              */
30317             "toggle" : true,
30318         /**
30319              * @event mouseover
30320              * Fires when the mouse hovers over the button
30321              * @param {Button} this
30322              * @param {Event} e The event object
30323              */
30324         'mouseover' : true,
30325         /**
30326              * @event mouseout
30327              * Fires when the mouse exits the button
30328              * @param {Button} this
30329              * @param {Event} e The event object
30330              */
30331         'mouseout': true,
30332          /**
30333              * @event render
30334              * Fires when the button is rendered
30335              * @param {Button} this
30336              */
30337         'render': true
30338     });
30339     if(this.menu){
30340         this.menu = Roo.menu.MenuMgr.get(this.menu);
30341     }
30342     // register listeners first!!  - so render can be captured..
30343     Roo.util.Observable.call(this);
30344     if(renderTo){
30345         this.render(renderTo);
30346     }
30347     
30348   
30349 };
30350
30351 Roo.extend(Roo.Button, Roo.util.Observable, {
30352     /**
30353      * 
30354      */
30355     
30356     /**
30357      * Read-only. True if this button is hidden
30358      * @type Boolean
30359      */
30360     hidden : false,
30361     /**
30362      * Read-only. True if this button is disabled
30363      * @type Boolean
30364      */
30365     disabled : false,
30366     /**
30367      * Read-only. True if this button is pressed (only if enableToggle = true)
30368      * @type Boolean
30369      */
30370     pressed : false,
30371
30372     /**
30373      * @cfg {Number} tabIndex 
30374      * The DOM tabIndex for this button (defaults to undefined)
30375      */
30376     tabIndex : undefined,
30377
30378     /**
30379      * @cfg {Boolean} enableToggle
30380      * True to enable pressed/not pressed toggling (defaults to false)
30381      */
30382     enableToggle: false,
30383     /**
30384      * @cfg {Roo.menu.Menu} menu
30385      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30386      */
30387     menu : undefined,
30388     /**
30389      * @cfg {String} menuAlign
30390      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30391      */
30392     menuAlign : "tl-bl?",
30393
30394     /**
30395      * @cfg {String} iconCls
30396      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30397      */
30398     iconCls : undefined,
30399     /**
30400      * @cfg {String} type
30401      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30402      */
30403     type : 'button',
30404
30405     // private
30406     menuClassTarget: 'tr',
30407
30408     /**
30409      * @cfg {String} clickEvent
30410      * The type of event to map to the button's event handler (defaults to 'click')
30411      */
30412     clickEvent : 'click',
30413
30414     /**
30415      * @cfg {Boolean} handleMouseEvents
30416      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30417      */
30418     handleMouseEvents : true,
30419
30420     /**
30421      * @cfg {String} tooltipType
30422      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30423      */
30424     tooltipType : 'qtip',
30425
30426     /**
30427      * @cfg {String} cls
30428      * A CSS class to apply to the button's main element.
30429      */
30430     
30431     /**
30432      * @cfg {Roo.Template} template (Optional)
30433      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30434      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30435      * require code modifications if required elements (e.g. a button) aren't present.
30436      */
30437
30438     // private
30439     render : function(renderTo){
30440         var btn;
30441         if(this.hideParent){
30442             this.parentEl = Roo.get(renderTo);
30443         }
30444         if(!this.dhconfig){
30445             if(!this.template){
30446                 if(!Roo.Button.buttonTemplate){
30447                     // hideous table template
30448                     Roo.Button.buttonTemplate = new Roo.Template(
30449                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30450                         '<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>',
30451                         "</tr></tbody></table>");
30452                 }
30453                 this.template = Roo.Button.buttonTemplate;
30454             }
30455             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30456             var btnEl = btn.child("button:first");
30457             btnEl.on('focus', this.onFocus, this);
30458             btnEl.on('blur', this.onBlur, this);
30459             if(this.cls){
30460                 btn.addClass(this.cls);
30461             }
30462             if(this.icon){
30463                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30464             }
30465             if(this.iconCls){
30466                 btnEl.addClass(this.iconCls);
30467                 if(!this.cls){
30468                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30469                 }
30470             }
30471             if(this.tabIndex !== undefined){
30472                 btnEl.dom.tabIndex = this.tabIndex;
30473             }
30474             if(this.tooltip){
30475                 if(typeof this.tooltip == 'object'){
30476                     Roo.QuickTips.tips(Roo.apply({
30477                           target: btnEl.id
30478                     }, this.tooltip));
30479                 } else {
30480                     btnEl.dom[this.tooltipType] = this.tooltip;
30481                 }
30482             }
30483         }else{
30484             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30485         }
30486         this.el = btn;
30487         if(this.id){
30488             this.el.dom.id = this.el.id = this.id;
30489         }
30490         if(this.menu){
30491             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30492             this.menu.on("show", this.onMenuShow, this);
30493             this.menu.on("hide", this.onMenuHide, this);
30494         }
30495         btn.addClass("x-btn");
30496         if(Roo.isIE && !Roo.isIE7){
30497             this.autoWidth.defer(1, this);
30498         }else{
30499             this.autoWidth();
30500         }
30501         if(this.handleMouseEvents){
30502             btn.on("mouseover", this.onMouseOver, this);
30503             btn.on("mouseout", this.onMouseOut, this);
30504             btn.on("mousedown", this.onMouseDown, this);
30505         }
30506         btn.on(this.clickEvent, this.onClick, this);
30507         //btn.on("mouseup", this.onMouseUp, this);
30508         if(this.hidden){
30509             this.hide();
30510         }
30511         if(this.disabled){
30512             this.disable();
30513         }
30514         Roo.ButtonToggleMgr.register(this);
30515         if(this.pressed){
30516             this.el.addClass("x-btn-pressed");
30517         }
30518         if(this.repeat){
30519             var repeater = new Roo.util.ClickRepeater(btn,
30520                 typeof this.repeat == "object" ? this.repeat : {}
30521             );
30522             repeater.on("click", this.onClick,  this);
30523         }
30524         
30525         this.fireEvent('render', this);
30526         
30527     },
30528     /**
30529      * Returns the button's underlying element
30530      * @return {Roo.Element} The element
30531      */
30532     getEl : function(){
30533         return this.el;  
30534     },
30535     
30536     /**
30537      * Destroys this Button and removes any listeners.
30538      */
30539     destroy : function(){
30540         Roo.ButtonToggleMgr.unregister(this);
30541         this.el.removeAllListeners();
30542         this.purgeListeners();
30543         this.el.remove();
30544     },
30545
30546     // private
30547     autoWidth : function(){
30548         if(this.el){
30549             this.el.setWidth("auto");
30550             if(Roo.isIE7 && Roo.isStrict){
30551                 var ib = this.el.child('button');
30552                 if(ib && ib.getWidth() > 20){
30553                     ib.clip();
30554                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30555                 }
30556             }
30557             if(this.minWidth){
30558                 if(this.hidden){
30559                     this.el.beginMeasure();
30560                 }
30561                 if(this.el.getWidth() < this.minWidth){
30562                     this.el.setWidth(this.minWidth);
30563                 }
30564                 if(this.hidden){
30565                     this.el.endMeasure();
30566                 }
30567             }
30568         }
30569     },
30570
30571     /**
30572      * Assigns this button's click handler
30573      * @param {Function} handler The function to call when the button is clicked
30574      * @param {Object} scope (optional) Scope for the function passed in
30575      */
30576     setHandler : function(handler, scope){
30577         this.handler = handler;
30578         this.scope = scope;  
30579     },
30580     
30581     /**
30582      * Sets this button's text
30583      * @param {String} text The button text
30584      */
30585     setText : function(text){
30586         this.text = text;
30587         if(this.el){
30588             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30589         }
30590         this.autoWidth();
30591     },
30592     
30593     /**
30594      * Gets the text for this button
30595      * @return {String} The button text
30596      */
30597     getText : function(){
30598         return this.text;  
30599     },
30600     
30601     /**
30602      * Show this button
30603      */
30604     show: function(){
30605         this.hidden = false;
30606         if(this.el){
30607             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30608         }
30609     },
30610     
30611     /**
30612      * Hide this button
30613      */
30614     hide: function(){
30615         this.hidden = true;
30616         if(this.el){
30617             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30618         }
30619     },
30620     
30621     /**
30622      * Convenience function for boolean show/hide
30623      * @param {Boolean} visible True to show, false to hide
30624      */
30625     setVisible: function(visible){
30626         if(visible) {
30627             this.show();
30628         }else{
30629             this.hide();
30630         }
30631     },
30632     
30633     /**
30634      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30635      * @param {Boolean} state (optional) Force a particular state
30636      */
30637     toggle : function(state){
30638         state = state === undefined ? !this.pressed : state;
30639         if(state != this.pressed){
30640             if(state){
30641                 this.el.addClass("x-btn-pressed");
30642                 this.pressed = true;
30643                 this.fireEvent("toggle", this, true);
30644             }else{
30645                 this.el.removeClass("x-btn-pressed");
30646                 this.pressed = false;
30647                 this.fireEvent("toggle", this, false);
30648             }
30649             if(this.toggleHandler){
30650                 this.toggleHandler.call(this.scope || this, this, state);
30651             }
30652         }
30653     },
30654     
30655     /**
30656      * Focus the button
30657      */
30658     focus : function(){
30659         this.el.child('button:first').focus();
30660     },
30661     
30662     /**
30663      * Disable this button
30664      */
30665     disable : function(){
30666         if(this.el){
30667             this.el.addClass("x-btn-disabled");
30668         }
30669         this.disabled = true;
30670     },
30671     
30672     /**
30673      * Enable this button
30674      */
30675     enable : function(){
30676         if(this.el){
30677             this.el.removeClass("x-btn-disabled");
30678         }
30679         this.disabled = false;
30680     },
30681
30682     /**
30683      * Convenience function for boolean enable/disable
30684      * @param {Boolean} enabled True to enable, false to disable
30685      */
30686     setDisabled : function(v){
30687         this[v !== true ? "enable" : "disable"]();
30688     },
30689
30690     // private
30691     onClick : function(e)
30692     {
30693         if(e){
30694             e.preventDefault();
30695         }
30696         if(e.button != 0){
30697             return;
30698         }
30699         if(!this.disabled){
30700             if(this.enableToggle){
30701                 this.toggle();
30702             }
30703             if(this.menu && !this.menu.isVisible()){
30704                 this.menu.show(this.el, this.menuAlign);
30705             }
30706             this.fireEvent("click", this, e);
30707             if(this.handler){
30708                 this.el.removeClass("x-btn-over");
30709                 this.handler.call(this.scope || this, this, e);
30710             }
30711         }
30712     },
30713     // private
30714     onMouseOver : function(e){
30715         if(!this.disabled){
30716             this.el.addClass("x-btn-over");
30717             this.fireEvent('mouseover', this, e);
30718         }
30719     },
30720     // private
30721     onMouseOut : function(e){
30722         if(!e.within(this.el,  true)){
30723             this.el.removeClass("x-btn-over");
30724             this.fireEvent('mouseout', this, e);
30725         }
30726     },
30727     // private
30728     onFocus : function(e){
30729         if(!this.disabled){
30730             this.el.addClass("x-btn-focus");
30731         }
30732     },
30733     // private
30734     onBlur : function(e){
30735         this.el.removeClass("x-btn-focus");
30736     },
30737     // private
30738     onMouseDown : function(e){
30739         if(!this.disabled && e.button == 0){
30740             this.el.addClass("x-btn-click");
30741             Roo.get(document).on('mouseup', this.onMouseUp, this);
30742         }
30743     },
30744     // private
30745     onMouseUp : function(e){
30746         if(e.button == 0){
30747             this.el.removeClass("x-btn-click");
30748             Roo.get(document).un('mouseup', this.onMouseUp, this);
30749         }
30750     },
30751     // private
30752     onMenuShow : function(e){
30753         this.el.addClass("x-btn-menu-active");
30754     },
30755     // private
30756     onMenuHide : function(e){
30757         this.el.removeClass("x-btn-menu-active");
30758     }   
30759 });
30760
30761 // Private utility class used by Button
30762 Roo.ButtonToggleMgr = function(){
30763    var groups = {};
30764    
30765    function toggleGroup(btn, state){
30766        if(state){
30767            var g = groups[btn.toggleGroup];
30768            for(var i = 0, l = g.length; i < l; i++){
30769                if(g[i] != btn){
30770                    g[i].toggle(false);
30771                }
30772            }
30773        }
30774    }
30775    
30776    return {
30777        register : function(btn){
30778            if(!btn.toggleGroup){
30779                return;
30780            }
30781            var g = groups[btn.toggleGroup];
30782            if(!g){
30783                g = groups[btn.toggleGroup] = [];
30784            }
30785            g.push(btn);
30786            btn.on("toggle", toggleGroup);
30787        },
30788        
30789        unregister : function(btn){
30790            if(!btn.toggleGroup){
30791                return;
30792            }
30793            var g = groups[btn.toggleGroup];
30794            if(g){
30795                g.remove(btn);
30796                btn.un("toggle", toggleGroup);
30797            }
30798        }
30799    };
30800 }();/*
30801  * Based on:
30802  * Ext JS Library 1.1.1
30803  * Copyright(c) 2006-2007, Ext JS, LLC.
30804  *
30805  * Originally Released Under LGPL - original licence link has changed is not relivant.
30806  *
30807  * Fork - LGPL
30808  * <script type="text/javascript">
30809  */
30810  
30811 /**
30812  * @class Roo.SplitButton
30813  * @extends Roo.Button
30814  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30815  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30816  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30817  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30818  * @cfg {String} arrowTooltip The title attribute of the arrow
30819  * @constructor
30820  * Create a new menu button
30821  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30822  * @param {Object} config The config object
30823  */
30824 Roo.SplitButton = function(renderTo, config){
30825     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30826     /**
30827      * @event arrowclick
30828      * Fires when this button's arrow is clicked
30829      * @param {SplitButton} this
30830      * @param {EventObject} e The click event
30831      */
30832     this.addEvents({"arrowclick":true});
30833 };
30834
30835 Roo.extend(Roo.SplitButton, Roo.Button, {
30836     render : function(renderTo){
30837         // this is one sweet looking template!
30838         var tpl = new Roo.Template(
30839             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30840             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30841             '<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>',
30842             "</tbody></table></td><td>",
30843             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30844             '<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>',
30845             "</tbody></table></td></tr></table>"
30846         );
30847         var btn = tpl.append(renderTo, [this.text, this.type], true);
30848         var btnEl = btn.child("button");
30849         if(this.cls){
30850             btn.addClass(this.cls);
30851         }
30852         if(this.icon){
30853             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30854         }
30855         if(this.iconCls){
30856             btnEl.addClass(this.iconCls);
30857             if(!this.cls){
30858                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30859             }
30860         }
30861         this.el = btn;
30862         if(this.handleMouseEvents){
30863             btn.on("mouseover", this.onMouseOver, this);
30864             btn.on("mouseout", this.onMouseOut, this);
30865             btn.on("mousedown", this.onMouseDown, this);
30866             btn.on("mouseup", this.onMouseUp, this);
30867         }
30868         btn.on(this.clickEvent, this.onClick, this);
30869         if(this.tooltip){
30870             if(typeof this.tooltip == 'object'){
30871                 Roo.QuickTips.tips(Roo.apply({
30872                       target: btnEl.id
30873                 }, this.tooltip));
30874             } else {
30875                 btnEl.dom[this.tooltipType] = this.tooltip;
30876             }
30877         }
30878         if(this.arrowTooltip){
30879             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30880         }
30881         if(this.hidden){
30882             this.hide();
30883         }
30884         if(this.disabled){
30885             this.disable();
30886         }
30887         if(this.pressed){
30888             this.el.addClass("x-btn-pressed");
30889         }
30890         if(Roo.isIE && !Roo.isIE7){
30891             this.autoWidth.defer(1, this);
30892         }else{
30893             this.autoWidth();
30894         }
30895         if(this.menu){
30896             this.menu.on("show", this.onMenuShow, this);
30897             this.menu.on("hide", this.onMenuHide, this);
30898         }
30899         this.fireEvent('render', this);
30900     },
30901
30902     // private
30903     autoWidth : function(){
30904         if(this.el){
30905             var tbl = this.el.child("table:first");
30906             var tbl2 = this.el.child("table:last");
30907             this.el.setWidth("auto");
30908             tbl.setWidth("auto");
30909             if(Roo.isIE7 && Roo.isStrict){
30910                 var ib = this.el.child('button:first');
30911                 if(ib && ib.getWidth() > 20){
30912                     ib.clip();
30913                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30914                 }
30915             }
30916             if(this.minWidth){
30917                 if(this.hidden){
30918                     this.el.beginMeasure();
30919                 }
30920                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30921                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30922                 }
30923                 if(this.hidden){
30924                     this.el.endMeasure();
30925                 }
30926             }
30927             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30928         } 
30929     },
30930     /**
30931      * Sets this button's click handler
30932      * @param {Function} handler The function to call when the button is clicked
30933      * @param {Object} scope (optional) Scope for the function passed above
30934      */
30935     setHandler : function(handler, scope){
30936         this.handler = handler;
30937         this.scope = scope;  
30938     },
30939     
30940     /**
30941      * Sets this button's arrow click handler
30942      * @param {Function} handler The function to call when the arrow is clicked
30943      * @param {Object} scope (optional) Scope for the function passed above
30944      */
30945     setArrowHandler : function(handler, scope){
30946         this.arrowHandler = handler;
30947         this.scope = scope;  
30948     },
30949     
30950     /**
30951      * Focus the button
30952      */
30953     focus : function(){
30954         if(this.el){
30955             this.el.child("button:first").focus();
30956         }
30957     },
30958
30959     // private
30960     onClick : function(e){
30961         e.preventDefault();
30962         if(!this.disabled){
30963             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30964                 if(this.menu && !this.menu.isVisible()){
30965                     this.menu.show(this.el, this.menuAlign);
30966                 }
30967                 this.fireEvent("arrowclick", this, e);
30968                 if(this.arrowHandler){
30969                     this.arrowHandler.call(this.scope || this, this, e);
30970                 }
30971             }else{
30972                 this.fireEvent("click", this, e);
30973                 if(this.handler){
30974                     this.handler.call(this.scope || this, this, e);
30975                 }
30976             }
30977         }
30978     },
30979     // private
30980     onMouseDown : function(e){
30981         if(!this.disabled){
30982             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30983         }
30984     },
30985     // private
30986     onMouseUp : function(e){
30987         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30988     }   
30989 });
30990
30991
30992 // backwards compat
30993 Roo.MenuButton = Roo.SplitButton;/*
30994  * Based on:
30995  * Ext JS Library 1.1.1
30996  * Copyright(c) 2006-2007, Ext JS, LLC.
30997  *
30998  * Originally Released Under LGPL - original licence link has changed is not relivant.
30999  *
31000  * Fork - LGPL
31001  * <script type="text/javascript">
31002  */
31003
31004 /**
31005  * @class Roo.Toolbar
31006  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31007  * Basic Toolbar class.
31008  * @constructor
31009  * Creates a new Toolbar
31010  * @param {Object} container The config object
31011  */ 
31012 Roo.Toolbar = function(container, buttons, config)
31013 {
31014     /// old consturctor format still supported..
31015     if(container instanceof Array){ // omit the container for later rendering
31016         buttons = container;
31017         config = buttons;
31018         container = null;
31019     }
31020     if (typeof(container) == 'object' && container.xtype) {
31021         config = container;
31022         container = config.container;
31023         buttons = config.buttons || []; // not really - use items!!
31024     }
31025     var xitems = [];
31026     if (config && config.items) {
31027         xitems = config.items;
31028         delete config.items;
31029     }
31030     Roo.apply(this, config);
31031     this.buttons = buttons;
31032     
31033     if(container){
31034         this.render(container);
31035     }
31036     this.xitems = xitems;
31037     Roo.each(xitems, function(b) {
31038         this.add(b);
31039     }, this);
31040     
31041 };
31042
31043 Roo.Toolbar.prototype = {
31044     /**
31045      * @cfg {Array} items
31046      * array of button configs or elements to add (will be converted to a MixedCollection)
31047      */
31048     items: false,
31049     /**
31050      * @cfg {String/HTMLElement/Element} container
31051      * The id or element that will contain the toolbar
31052      */
31053     // private
31054     render : function(ct){
31055         this.el = Roo.get(ct);
31056         if(this.cls){
31057             this.el.addClass(this.cls);
31058         }
31059         // using a table allows for vertical alignment
31060         // 100% width is needed by Safari...
31061         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31062         this.tr = this.el.child("tr", true);
31063         var autoId = 0;
31064         this.items = new Roo.util.MixedCollection(false, function(o){
31065             return o.id || ("item" + (++autoId));
31066         });
31067         if(this.buttons){
31068             this.add.apply(this, this.buttons);
31069             delete this.buttons;
31070         }
31071     },
31072
31073     /**
31074      * Adds element(s) to the toolbar -- this function takes a variable number of 
31075      * arguments of mixed type and adds them to the toolbar.
31076      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31077      * <ul>
31078      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31079      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31080      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31081      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31082      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31083      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31084      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31085      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31086      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31087      * </ul>
31088      * @param {Mixed} arg2
31089      * @param {Mixed} etc.
31090      */
31091     add : function(){
31092         var a = arguments, l = a.length;
31093         for(var i = 0; i < l; i++){
31094             this._add(a[i]);
31095         }
31096     },
31097     // private..
31098     _add : function(el) {
31099         
31100         if (el.xtype) {
31101             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31102         }
31103         
31104         if (el.applyTo){ // some kind of form field
31105             return this.addField(el);
31106         } 
31107         if (el.render){ // some kind of Toolbar.Item
31108             return this.addItem(el);
31109         }
31110         if (typeof el == "string"){ // string
31111             if(el == "separator" || el == "-"){
31112                 return this.addSeparator();
31113             }
31114             if (el == " "){
31115                 return this.addSpacer();
31116             }
31117             if(el == "->"){
31118                 return this.addFill();
31119             }
31120             return this.addText(el);
31121             
31122         }
31123         if(el.tagName){ // element
31124             return this.addElement(el);
31125         }
31126         if(typeof el == "object"){ // must be button config?
31127             return this.addButton(el);
31128         }
31129         // and now what?!?!
31130         return false;
31131         
31132     },
31133     
31134     /**
31135      * Add an Xtype element
31136      * @param {Object} xtype Xtype Object
31137      * @return {Object} created Object
31138      */
31139     addxtype : function(e){
31140         return this.add(e);  
31141     },
31142     
31143     /**
31144      * Returns the Element for this toolbar.
31145      * @return {Roo.Element}
31146      */
31147     getEl : function(){
31148         return this.el;  
31149     },
31150     
31151     /**
31152      * Adds a separator
31153      * @return {Roo.Toolbar.Item} The separator item
31154      */
31155     addSeparator : function(){
31156         return this.addItem(new Roo.Toolbar.Separator());
31157     },
31158
31159     /**
31160      * Adds a spacer element
31161      * @return {Roo.Toolbar.Spacer} The spacer item
31162      */
31163     addSpacer : function(){
31164         return this.addItem(new Roo.Toolbar.Spacer());
31165     },
31166
31167     /**
31168      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31169      * @return {Roo.Toolbar.Fill} The fill item
31170      */
31171     addFill : function(){
31172         return this.addItem(new Roo.Toolbar.Fill());
31173     },
31174
31175     /**
31176      * Adds any standard HTML element to the toolbar
31177      * @param {String/HTMLElement/Element} el The element or id of the element to add
31178      * @return {Roo.Toolbar.Item} The element's item
31179      */
31180     addElement : function(el){
31181         return this.addItem(new Roo.Toolbar.Item(el));
31182     },
31183     /**
31184      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31185      * @type Roo.util.MixedCollection  
31186      */
31187     items : false,
31188      
31189     /**
31190      * Adds any Toolbar.Item or subclass
31191      * @param {Roo.Toolbar.Item} item
31192      * @return {Roo.Toolbar.Item} The item
31193      */
31194     addItem : function(item){
31195         var td = this.nextBlock();
31196         item.render(td);
31197         this.items.add(item);
31198         return item;
31199     },
31200     
31201     /**
31202      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31203      * @param {Object/Array} config A button config or array of configs
31204      * @return {Roo.Toolbar.Button/Array}
31205      */
31206     addButton : function(config){
31207         if(config instanceof Array){
31208             var buttons = [];
31209             for(var i = 0, len = config.length; i < len; i++) {
31210                 buttons.push(this.addButton(config[i]));
31211             }
31212             return buttons;
31213         }
31214         var b = config;
31215         if(!(config instanceof Roo.Toolbar.Button)){
31216             b = config.split ?
31217                 new Roo.Toolbar.SplitButton(config) :
31218                 new Roo.Toolbar.Button(config);
31219         }
31220         var td = this.nextBlock();
31221         b.render(td);
31222         this.items.add(b);
31223         return b;
31224     },
31225     
31226     /**
31227      * Adds text to the toolbar
31228      * @param {String} text The text to add
31229      * @return {Roo.Toolbar.Item} The element's item
31230      */
31231     addText : function(text){
31232         return this.addItem(new Roo.Toolbar.TextItem(text));
31233     },
31234     
31235     /**
31236      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31237      * @param {Number} index The index where the item is to be inserted
31238      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31239      * @return {Roo.Toolbar.Button/Item}
31240      */
31241     insertButton : function(index, item){
31242         if(item instanceof Array){
31243             var buttons = [];
31244             for(var i = 0, len = item.length; i < len; i++) {
31245                buttons.push(this.insertButton(index + i, item[i]));
31246             }
31247             return buttons;
31248         }
31249         if (!(item instanceof Roo.Toolbar.Button)){
31250            item = new Roo.Toolbar.Button(item);
31251         }
31252         var td = document.createElement("td");
31253         this.tr.insertBefore(td, this.tr.childNodes[index]);
31254         item.render(td);
31255         this.items.insert(index, item);
31256         return item;
31257     },
31258     
31259     /**
31260      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31261      * @param {Object} config
31262      * @return {Roo.Toolbar.Item} The element's item
31263      */
31264     addDom : function(config, returnEl){
31265         var td = this.nextBlock();
31266         Roo.DomHelper.overwrite(td, config);
31267         var ti = new Roo.Toolbar.Item(td.firstChild);
31268         ti.render(td);
31269         this.items.add(ti);
31270         return ti;
31271     },
31272
31273     /**
31274      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31275      * @type Roo.util.MixedCollection  
31276      */
31277     fields : false,
31278     
31279     /**
31280      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31281      * Note: the field should not have been rendered yet. For a field that has already been
31282      * rendered, use {@link #addElement}.
31283      * @param {Roo.form.Field} field
31284      * @return {Roo.ToolbarItem}
31285      */
31286      
31287       
31288     addField : function(field) {
31289         if (!this.fields) {
31290             var autoId = 0;
31291             this.fields = new Roo.util.MixedCollection(false, function(o){
31292                 return o.id || ("item" + (++autoId));
31293             });
31294
31295         }
31296         
31297         var td = this.nextBlock();
31298         field.render(td);
31299         var ti = new Roo.Toolbar.Item(td.firstChild);
31300         ti.render(td);
31301         this.items.add(ti);
31302         this.fields.add(field);
31303         return ti;
31304     },
31305     /**
31306      * Hide the toolbar
31307      * @method hide
31308      */
31309      
31310       
31311     hide : function()
31312     {
31313         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31314         this.el.child('div').hide();
31315     },
31316     /**
31317      * Show the toolbar
31318      * @method show
31319      */
31320     show : function()
31321     {
31322         this.el.child('div').show();
31323     },
31324       
31325     // private
31326     nextBlock : function(){
31327         var td = document.createElement("td");
31328         this.tr.appendChild(td);
31329         return td;
31330     },
31331
31332     // private
31333     destroy : function(){
31334         if(this.items){ // rendered?
31335             Roo.destroy.apply(Roo, this.items.items);
31336         }
31337         if(this.fields){ // rendered?
31338             Roo.destroy.apply(Roo, this.fields.items);
31339         }
31340         Roo.Element.uncache(this.el, this.tr);
31341     }
31342 };
31343
31344 /**
31345  * @class Roo.Toolbar.Item
31346  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31347  * @constructor
31348  * Creates a new Item
31349  * @param {HTMLElement} el 
31350  */
31351 Roo.Toolbar.Item = function(el){
31352     var cfg = {};
31353     if (typeof (el.xtype) != 'undefined') {
31354         cfg = el;
31355         el = cfg.el;
31356     }
31357     
31358     this.el = Roo.getDom(el);
31359     this.id = Roo.id(this.el);
31360     this.hidden = false;
31361     
31362     this.addEvents({
31363          /**
31364              * @event render
31365              * Fires when the button is rendered
31366              * @param {Button} this
31367              */
31368         'render': true
31369     });
31370     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31371 };
31372 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31373 //Roo.Toolbar.Item.prototype = {
31374     
31375     /**
31376      * Get this item's HTML Element
31377      * @return {HTMLElement}
31378      */
31379     getEl : function(){
31380        return this.el;  
31381     },
31382
31383     // private
31384     render : function(td){
31385         
31386          this.td = td;
31387         td.appendChild(this.el);
31388         
31389         this.fireEvent('render', this);
31390     },
31391     
31392     /**
31393      * Removes and destroys this item.
31394      */
31395     destroy : function(){
31396         this.td.parentNode.removeChild(this.td);
31397     },
31398     
31399     /**
31400      * Shows this item.
31401      */
31402     show: function(){
31403         this.hidden = false;
31404         this.td.style.display = "";
31405     },
31406     
31407     /**
31408      * Hides this item.
31409      */
31410     hide: function(){
31411         this.hidden = true;
31412         this.td.style.display = "none";
31413     },
31414     
31415     /**
31416      * Convenience function for boolean show/hide.
31417      * @param {Boolean} visible true to show/false to hide
31418      */
31419     setVisible: function(visible){
31420         if(visible) {
31421             this.show();
31422         }else{
31423             this.hide();
31424         }
31425     },
31426     
31427     /**
31428      * Try to focus this item.
31429      */
31430     focus : function(){
31431         Roo.fly(this.el).focus();
31432     },
31433     
31434     /**
31435      * Disables this item.
31436      */
31437     disable : function(){
31438         Roo.fly(this.td).addClass("x-item-disabled");
31439         this.disabled = true;
31440         this.el.disabled = true;
31441     },
31442     
31443     /**
31444      * Enables this item.
31445      */
31446     enable : function(){
31447         Roo.fly(this.td).removeClass("x-item-disabled");
31448         this.disabled = false;
31449         this.el.disabled = false;
31450     }
31451 });
31452
31453
31454 /**
31455  * @class Roo.Toolbar.Separator
31456  * @extends Roo.Toolbar.Item
31457  * A simple toolbar separator class
31458  * @constructor
31459  * Creates a new Separator
31460  */
31461 Roo.Toolbar.Separator = function(cfg){
31462     
31463     var s = document.createElement("span");
31464     s.className = "ytb-sep";
31465     if (cfg) {
31466         cfg.el = s;
31467     }
31468     
31469     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31470 };
31471 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31472     enable:Roo.emptyFn,
31473     disable:Roo.emptyFn,
31474     focus:Roo.emptyFn
31475 });
31476
31477 /**
31478  * @class Roo.Toolbar.Spacer
31479  * @extends Roo.Toolbar.Item
31480  * A simple element that adds extra horizontal space to a toolbar.
31481  * @constructor
31482  * Creates a new Spacer
31483  */
31484 Roo.Toolbar.Spacer = function(cfg){
31485     var s = document.createElement("div");
31486     s.className = "ytb-spacer";
31487     if (cfg) {
31488         cfg.el = s;
31489     }
31490     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31491 };
31492 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31493     enable:Roo.emptyFn,
31494     disable:Roo.emptyFn,
31495     focus:Roo.emptyFn
31496 });
31497
31498 /**
31499  * @class Roo.Toolbar.Fill
31500  * @extends Roo.Toolbar.Spacer
31501  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31502  * @constructor
31503  * Creates a new Spacer
31504  */
31505 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31506     // private
31507     render : function(td){
31508         td.style.width = '100%';
31509         Roo.Toolbar.Fill.superclass.render.call(this, td);
31510     }
31511 });
31512
31513 /**
31514  * @class Roo.Toolbar.TextItem
31515  * @extends Roo.Toolbar.Item
31516  * A simple class that renders text directly into a toolbar.
31517  * @constructor
31518  * Creates a new TextItem
31519  * @cfg {string} text 
31520  */
31521 Roo.Toolbar.TextItem = function(cfg){
31522     var  text = cfg || "";
31523     if (typeof(cfg) == 'object') {
31524         text = cfg.text || "";
31525     }  else {
31526         cfg = null;
31527     }
31528     var s = document.createElement("span");
31529     s.className = "ytb-text";
31530     s.innerHTML = text;
31531     if (cfg) {
31532         cfg.el  = s;
31533     }
31534     
31535     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31536 };
31537 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31538     
31539      
31540     enable:Roo.emptyFn,
31541     disable:Roo.emptyFn,
31542     focus:Roo.emptyFn,
31543      /**
31544      * Shows this button
31545      */
31546     show: function(){
31547         this.hidden = false;
31548         this.el.style.display = "";
31549     },
31550     
31551     /**
31552      * Hides this button
31553      */
31554     hide: function(){
31555         this.hidden = true;
31556         this.el.style.display = "none";
31557     }
31558     
31559 });
31560
31561 /**
31562  * @class Roo.Toolbar.Button
31563  * @extends Roo.Button
31564  * A button that renders into a toolbar.
31565  * @constructor
31566  * Creates a new Button
31567  * @param {Object} config A standard {@link Roo.Button} config object
31568  */
31569 Roo.Toolbar.Button = function(config){
31570     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31571 };
31572 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31573 {
31574     
31575     
31576     render : function(td){
31577         this.td = td;
31578         Roo.Toolbar.Button.superclass.render.call(this, td);
31579     },
31580     
31581     /**
31582      * Removes and destroys this button
31583      */
31584     destroy : function(){
31585         Roo.Toolbar.Button.superclass.destroy.call(this);
31586         this.td.parentNode.removeChild(this.td);
31587     },
31588     
31589     /**
31590      * Shows this button
31591      */
31592     show: function(){
31593         this.hidden = false;
31594         this.td.style.display = "";
31595     },
31596     
31597     /**
31598      * Hides this button
31599      */
31600     hide: function(){
31601         this.hidden = true;
31602         this.td.style.display = "none";
31603     },
31604
31605     /**
31606      * Disables this item
31607      */
31608     disable : function(){
31609         Roo.fly(this.td).addClass("x-item-disabled");
31610         this.disabled = true;
31611     },
31612
31613     /**
31614      * Enables this item
31615      */
31616     enable : function(){
31617         Roo.fly(this.td).removeClass("x-item-disabled");
31618         this.disabled = false;
31619     }
31620 });
31621 // backwards compat
31622 Roo.ToolbarButton = Roo.Toolbar.Button;
31623
31624 /**
31625  * @class Roo.Toolbar.SplitButton
31626  * @extends Roo.SplitButton
31627  * A menu button that renders into a toolbar.
31628  * @constructor
31629  * Creates a new SplitButton
31630  * @param {Object} config A standard {@link Roo.SplitButton} config object
31631  */
31632 Roo.Toolbar.SplitButton = function(config){
31633     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31634 };
31635 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31636     render : function(td){
31637         this.td = td;
31638         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31639     },
31640     
31641     /**
31642      * Removes and destroys this button
31643      */
31644     destroy : function(){
31645         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31646         this.td.parentNode.removeChild(this.td);
31647     },
31648     
31649     /**
31650      * Shows this button
31651      */
31652     show: function(){
31653         this.hidden = false;
31654         this.td.style.display = "";
31655     },
31656     
31657     /**
31658      * Hides this button
31659      */
31660     hide: function(){
31661         this.hidden = true;
31662         this.td.style.display = "none";
31663     }
31664 });
31665
31666 // backwards compat
31667 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31668  * Based on:
31669  * Ext JS Library 1.1.1
31670  * Copyright(c) 2006-2007, Ext JS, LLC.
31671  *
31672  * Originally Released Under LGPL - original licence link has changed is not relivant.
31673  *
31674  * Fork - LGPL
31675  * <script type="text/javascript">
31676  */
31677  
31678 /**
31679  * @class Roo.PagingToolbar
31680  * @extends Roo.Toolbar
31681  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31682  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31683  * @constructor
31684  * Create a new PagingToolbar
31685  * @param {Object} config The config object
31686  */
31687 Roo.PagingToolbar = function(el, ds, config)
31688 {
31689     // old args format still supported... - xtype is prefered..
31690     if (typeof(el) == 'object' && el.xtype) {
31691         // created from xtype...
31692         config = el;
31693         ds = el.dataSource;
31694         el = config.container;
31695     }
31696     var items = [];
31697     if (config.items) {
31698         items = config.items;
31699         config.items = [];
31700     }
31701     
31702     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31703     this.ds = ds;
31704     this.cursor = 0;
31705     this.renderButtons(this.el);
31706     this.bind(ds);
31707     
31708     // supprot items array.
31709    
31710     Roo.each(items, function(e) {
31711         this.add(Roo.factory(e));
31712     },this);
31713     
31714 };
31715
31716 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31717    
31718     /**
31719      * @cfg {String/HTMLElement/Element} container
31720      * container The id or element that will contain the toolbar
31721      */
31722     /**
31723      * @cfg {Boolean} displayInfo
31724      * True to display the displayMsg (defaults to false)
31725      */
31726     
31727     
31728     /**
31729      * @cfg {Number} pageSize
31730      * The number of records to display per page (defaults to 20)
31731      */
31732     pageSize: 20,
31733     /**
31734      * @cfg {String} displayMsg
31735      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31736      */
31737     displayMsg : 'Displaying {0} - {1} of {2}',
31738     /**
31739      * @cfg {String} emptyMsg
31740      * The message to display when no records are found (defaults to "No data to display")
31741      */
31742     emptyMsg : 'No data to display',
31743     /**
31744      * Customizable piece of the default paging text (defaults to "Page")
31745      * @type String
31746      */
31747     beforePageText : "Page",
31748     /**
31749      * Customizable piece of the default paging text (defaults to "of %0")
31750      * @type String
31751      */
31752     afterPageText : "of {0}",
31753     /**
31754      * Customizable piece of the default paging text (defaults to "First Page")
31755      * @type String
31756      */
31757     firstText : "First Page",
31758     /**
31759      * Customizable piece of the default paging text (defaults to "Previous Page")
31760      * @type String
31761      */
31762     prevText : "Previous Page",
31763     /**
31764      * Customizable piece of the default paging text (defaults to "Next Page")
31765      * @type String
31766      */
31767     nextText : "Next Page",
31768     /**
31769      * Customizable piece of the default paging text (defaults to "Last Page")
31770      * @type String
31771      */
31772     lastText : "Last Page",
31773     /**
31774      * Customizable piece of the default paging text (defaults to "Refresh")
31775      * @type String
31776      */
31777     refreshText : "Refresh",
31778
31779     // private
31780     renderButtons : function(el){
31781         Roo.PagingToolbar.superclass.render.call(this, el);
31782         this.first = this.addButton({
31783             tooltip: this.firstText,
31784             cls: "x-btn-icon x-grid-page-first",
31785             disabled: true,
31786             handler: this.onClick.createDelegate(this, ["first"])
31787         });
31788         this.prev = this.addButton({
31789             tooltip: this.prevText,
31790             cls: "x-btn-icon x-grid-page-prev",
31791             disabled: true,
31792             handler: this.onClick.createDelegate(this, ["prev"])
31793         });
31794         //this.addSeparator();
31795         this.add(this.beforePageText);
31796         this.field = Roo.get(this.addDom({
31797            tag: "input",
31798            type: "text",
31799            size: "3",
31800            value: "1",
31801            cls: "x-grid-page-number"
31802         }).el);
31803         this.field.on("keydown", this.onPagingKeydown, this);
31804         this.field.on("focus", function(){this.dom.select();});
31805         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31806         this.field.setHeight(18);
31807         //this.addSeparator();
31808         this.next = this.addButton({
31809             tooltip: this.nextText,
31810             cls: "x-btn-icon x-grid-page-next",
31811             disabled: true,
31812             handler: this.onClick.createDelegate(this, ["next"])
31813         });
31814         this.last = this.addButton({
31815             tooltip: this.lastText,
31816             cls: "x-btn-icon x-grid-page-last",
31817             disabled: true,
31818             handler: this.onClick.createDelegate(this, ["last"])
31819         });
31820         //this.addSeparator();
31821         this.loading = this.addButton({
31822             tooltip: this.refreshText,
31823             cls: "x-btn-icon x-grid-loading",
31824             handler: this.onClick.createDelegate(this, ["refresh"])
31825         });
31826
31827         if(this.displayInfo){
31828             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31829         }
31830     },
31831
31832     // private
31833     updateInfo : function(){
31834         if(this.displayEl){
31835             var count = this.ds.getCount();
31836             var msg = count == 0 ?
31837                 this.emptyMsg :
31838                 String.format(
31839                     this.displayMsg,
31840                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31841                 );
31842             this.displayEl.update(msg);
31843         }
31844     },
31845
31846     // private
31847     onLoad : function(ds, r, o){
31848        this.cursor = o.params ? o.params.start : 0;
31849        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31850
31851        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31852        this.field.dom.value = ap;
31853        this.first.setDisabled(ap == 1);
31854        this.prev.setDisabled(ap == 1);
31855        this.next.setDisabled(ap == ps);
31856        this.last.setDisabled(ap == ps);
31857        this.loading.enable();
31858        this.updateInfo();
31859     },
31860
31861     // private
31862     getPageData : function(){
31863         var total = this.ds.getTotalCount();
31864         return {
31865             total : total,
31866             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31867             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31868         };
31869     },
31870
31871     // private
31872     onLoadError : function(){
31873         this.loading.enable();
31874     },
31875
31876     // private
31877     onPagingKeydown : function(e){
31878         var k = e.getKey();
31879         var d = this.getPageData();
31880         if(k == e.RETURN){
31881             var v = this.field.dom.value, pageNum;
31882             if(!v || isNaN(pageNum = parseInt(v, 10))){
31883                 this.field.dom.value = d.activePage;
31884                 return;
31885             }
31886             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31887             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31888             e.stopEvent();
31889         }
31890         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))
31891         {
31892           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31893           this.field.dom.value = pageNum;
31894           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31895           e.stopEvent();
31896         }
31897         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31898         {
31899           var v = this.field.dom.value, pageNum; 
31900           var increment = (e.shiftKey) ? 10 : 1;
31901           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31902             increment *= -1;
31903           }
31904           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31905             this.field.dom.value = d.activePage;
31906             return;
31907           }
31908           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31909           {
31910             this.field.dom.value = parseInt(v, 10) + increment;
31911             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31912             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31913           }
31914           e.stopEvent();
31915         }
31916     },
31917
31918     // private
31919     beforeLoad : function(){
31920         if(this.loading){
31921             this.loading.disable();
31922         }
31923     },
31924
31925     // private
31926     onClick : function(which){
31927         var ds = this.ds;
31928         switch(which){
31929             case "first":
31930                 ds.load({params:{start: 0, limit: this.pageSize}});
31931             break;
31932             case "prev":
31933                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31934             break;
31935             case "next":
31936                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31937             break;
31938             case "last":
31939                 var total = ds.getTotalCount();
31940                 var extra = total % this.pageSize;
31941                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31942                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31943             break;
31944             case "refresh":
31945                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31946             break;
31947         }
31948     },
31949
31950     /**
31951      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31952      * @param {Roo.data.Store} store The data store to unbind
31953      */
31954     unbind : function(ds){
31955         ds.un("beforeload", this.beforeLoad, this);
31956         ds.un("load", this.onLoad, this);
31957         ds.un("loadexception", this.onLoadError, this);
31958         ds.un("remove", this.updateInfo, this);
31959         ds.un("add", this.updateInfo, this);
31960         this.ds = undefined;
31961     },
31962
31963     /**
31964      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31965      * @param {Roo.data.Store} store The data store to bind
31966      */
31967     bind : function(ds){
31968         ds.on("beforeload", this.beforeLoad, this);
31969         ds.on("load", this.onLoad, this);
31970         ds.on("loadexception", this.onLoadError, this);
31971         ds.on("remove", this.updateInfo, this);
31972         ds.on("add", this.updateInfo, this);
31973         this.ds = ds;
31974     }
31975 });/*
31976  * Based on:
31977  * Ext JS Library 1.1.1
31978  * Copyright(c) 2006-2007, Ext JS, LLC.
31979  *
31980  * Originally Released Under LGPL - original licence link has changed is not relivant.
31981  *
31982  * Fork - LGPL
31983  * <script type="text/javascript">
31984  */
31985
31986 /**
31987  * @class Roo.Resizable
31988  * @extends Roo.util.Observable
31989  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31990  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31991  * 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
31992  * the element will be wrapped for you automatically.</p>
31993  * <p>Here is the list of valid resize handles:</p>
31994  * <pre>
31995 Value   Description
31996 ------  -------------------
31997  'n'     north
31998  's'     south
31999  'e'     east
32000  'w'     west
32001  'nw'    northwest
32002  'sw'    southwest
32003  'se'    southeast
32004  'ne'    northeast
32005  'hd'    horizontal drag
32006  'all'   all
32007 </pre>
32008  * <p>Here's an example showing the creation of a typical Resizable:</p>
32009  * <pre><code>
32010 var resizer = new Roo.Resizable("element-id", {
32011     handles: 'all',
32012     minWidth: 200,
32013     minHeight: 100,
32014     maxWidth: 500,
32015     maxHeight: 400,
32016     pinned: true
32017 });
32018 resizer.on("resize", myHandler);
32019 </code></pre>
32020  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32021  * resizer.east.setDisplayed(false);</p>
32022  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32023  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32024  * resize operation's new size (defaults to [0, 0])
32025  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32026  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32027  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32028  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32029  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32030  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32031  * @cfg {Number} width The width of the element in pixels (defaults to null)
32032  * @cfg {Number} height The height of the element in pixels (defaults to null)
32033  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32034  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32035  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32036  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32037  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32038  * in favor of the handles config option (defaults to false)
32039  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32040  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32041  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32042  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32043  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32044  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32045  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32046  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32047  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32048  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32049  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32050  * @constructor
32051  * Create a new resizable component
32052  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32053  * @param {Object} config configuration options
32054   */
32055 Roo.Resizable = function(el, config)
32056 {
32057     this.el = Roo.get(el);
32058
32059     if(config && config.wrap){
32060         config.resizeChild = this.el;
32061         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32062         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32063         this.el.setStyle("overflow", "hidden");
32064         this.el.setPositioning(config.resizeChild.getPositioning());
32065         config.resizeChild.clearPositioning();
32066         if(!config.width || !config.height){
32067             var csize = config.resizeChild.getSize();
32068             this.el.setSize(csize.width, csize.height);
32069         }
32070         if(config.pinned && !config.adjustments){
32071             config.adjustments = "auto";
32072         }
32073     }
32074
32075     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32076     this.proxy.unselectable();
32077     this.proxy.enableDisplayMode('block');
32078
32079     Roo.apply(this, config);
32080
32081     if(this.pinned){
32082         this.disableTrackOver = true;
32083         this.el.addClass("x-resizable-pinned");
32084     }
32085     // if the element isn't positioned, make it relative
32086     var position = this.el.getStyle("position");
32087     if(position != "absolute" && position != "fixed"){
32088         this.el.setStyle("position", "relative");
32089     }
32090     if(!this.handles){ // no handles passed, must be legacy style
32091         this.handles = 's,e,se';
32092         if(this.multiDirectional){
32093             this.handles += ',n,w';
32094         }
32095     }
32096     if(this.handles == "all"){
32097         this.handles = "n s e w ne nw se sw";
32098     }
32099     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32100     var ps = Roo.Resizable.positions;
32101     for(var i = 0, len = hs.length; i < len; i++){
32102         if(hs[i] && ps[hs[i]]){
32103             var pos = ps[hs[i]];
32104             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32105         }
32106     }
32107     // legacy
32108     this.corner = this.southeast;
32109     
32110     // updateBox = the box can move..
32111     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32112         this.updateBox = true;
32113     }
32114
32115     this.activeHandle = null;
32116
32117     if(this.resizeChild){
32118         if(typeof this.resizeChild == "boolean"){
32119             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32120         }else{
32121             this.resizeChild = Roo.get(this.resizeChild, true);
32122         }
32123     }
32124     
32125     if(this.adjustments == "auto"){
32126         var rc = this.resizeChild;
32127         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32128         if(rc && (hw || hn)){
32129             rc.position("relative");
32130             rc.setLeft(hw ? hw.el.getWidth() : 0);
32131             rc.setTop(hn ? hn.el.getHeight() : 0);
32132         }
32133         this.adjustments = [
32134             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32135             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32136         ];
32137     }
32138
32139     if(this.draggable){
32140         this.dd = this.dynamic ?
32141             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32142         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32143     }
32144
32145     // public events
32146     this.addEvents({
32147         /**
32148          * @event beforeresize
32149          * Fired before resize is allowed. Set enabled to false to cancel resize.
32150          * @param {Roo.Resizable} this
32151          * @param {Roo.EventObject} e The mousedown event
32152          */
32153         "beforeresize" : true,
32154         /**
32155          * @event resizing
32156          * Fired a resizing.
32157          * @param {Roo.Resizable} this
32158          * @param {Number} x The new x position
32159          * @param {Number} y The new y position
32160          * @param {Number} w The new w width
32161          * @param {Number} h The new h hight
32162          * @param {Roo.EventObject} e The mouseup event
32163          */
32164         "resizing" : true,
32165         /**
32166          * @event resize
32167          * Fired after a resize.
32168          * @param {Roo.Resizable} this
32169          * @param {Number} width The new width
32170          * @param {Number} height The new height
32171          * @param {Roo.EventObject} e The mouseup event
32172          */
32173         "resize" : true
32174     });
32175
32176     if(this.width !== null && this.height !== null){
32177         this.resizeTo(this.width, this.height);
32178     }else{
32179         this.updateChildSize();
32180     }
32181     if(Roo.isIE){
32182         this.el.dom.style.zoom = 1;
32183     }
32184     Roo.Resizable.superclass.constructor.call(this);
32185 };
32186
32187 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32188         resizeChild : false,
32189         adjustments : [0, 0],
32190         minWidth : 5,
32191         minHeight : 5,
32192         maxWidth : 10000,
32193         maxHeight : 10000,
32194         enabled : true,
32195         animate : false,
32196         duration : .35,
32197         dynamic : false,
32198         handles : false,
32199         multiDirectional : false,
32200         disableTrackOver : false,
32201         easing : 'easeOutStrong',
32202         widthIncrement : 0,
32203         heightIncrement : 0,
32204         pinned : false,
32205         width : null,
32206         height : null,
32207         preserveRatio : false,
32208         transparent: false,
32209         minX: 0,
32210         minY: 0,
32211         draggable: false,
32212
32213         /**
32214          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32215          */
32216         constrainTo: undefined,
32217         /**
32218          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32219          */
32220         resizeRegion: undefined,
32221
32222
32223     /**
32224      * Perform a manual resize
32225      * @param {Number} width
32226      * @param {Number} height
32227      */
32228     resizeTo : function(width, height){
32229         this.el.setSize(width, height);
32230         this.updateChildSize();
32231         this.fireEvent("resize", this, width, height, null);
32232     },
32233
32234     // private
32235     startSizing : function(e, handle){
32236         this.fireEvent("beforeresize", this, e);
32237         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32238
32239             if(!this.overlay){
32240                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32241                 this.overlay.unselectable();
32242                 this.overlay.enableDisplayMode("block");
32243                 this.overlay.on("mousemove", this.onMouseMove, this);
32244                 this.overlay.on("mouseup", this.onMouseUp, this);
32245             }
32246             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32247
32248             this.resizing = true;
32249             this.startBox = this.el.getBox();
32250             this.startPoint = e.getXY();
32251             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32252                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32253
32254             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32255             this.overlay.show();
32256
32257             if(this.constrainTo) {
32258                 var ct = Roo.get(this.constrainTo);
32259                 this.resizeRegion = ct.getRegion().adjust(
32260                     ct.getFrameWidth('t'),
32261                     ct.getFrameWidth('l'),
32262                     -ct.getFrameWidth('b'),
32263                     -ct.getFrameWidth('r')
32264                 );
32265             }
32266
32267             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32268             this.proxy.show();
32269             this.proxy.setBox(this.startBox);
32270             if(!this.dynamic){
32271                 this.proxy.setStyle('visibility', 'visible');
32272             }
32273         }
32274     },
32275
32276     // private
32277     onMouseDown : function(handle, e){
32278         if(this.enabled){
32279             e.stopEvent();
32280             this.activeHandle = handle;
32281             this.startSizing(e, handle);
32282         }
32283     },
32284
32285     // private
32286     onMouseUp : function(e){
32287         var size = this.resizeElement();
32288         this.resizing = false;
32289         this.handleOut();
32290         this.overlay.hide();
32291         this.proxy.hide();
32292         this.fireEvent("resize", this, size.width, size.height, e);
32293     },
32294
32295     // private
32296     updateChildSize : function(){
32297         
32298         if(this.resizeChild){
32299             var el = this.el;
32300             var child = this.resizeChild;
32301             var adj = this.adjustments;
32302             if(el.dom.offsetWidth){
32303                 var b = el.getSize(true);
32304                 child.setSize(b.width+adj[0], b.height+adj[1]);
32305             }
32306             // Second call here for IE
32307             // The first call enables instant resizing and
32308             // the second call corrects scroll bars if they
32309             // exist
32310             if(Roo.isIE){
32311                 setTimeout(function(){
32312                     if(el.dom.offsetWidth){
32313                         var b = el.getSize(true);
32314                         child.setSize(b.width+adj[0], b.height+adj[1]);
32315                     }
32316                 }, 10);
32317             }
32318         }
32319     },
32320
32321     // private
32322     snap : function(value, inc, min){
32323         if(!inc || !value) {
32324             return value;
32325         }
32326         var newValue = value;
32327         var m = value % inc;
32328         if(m > 0){
32329             if(m > (inc/2)){
32330                 newValue = value + (inc-m);
32331             }else{
32332                 newValue = value - m;
32333             }
32334         }
32335         return Math.max(min, newValue);
32336     },
32337
32338     // private
32339     resizeElement : function(){
32340         var box = this.proxy.getBox();
32341         if(this.updateBox){
32342             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32343         }else{
32344             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32345         }
32346         this.updateChildSize();
32347         if(!this.dynamic){
32348             this.proxy.hide();
32349         }
32350         return box;
32351     },
32352
32353     // private
32354     constrain : function(v, diff, m, mx){
32355         if(v - diff < m){
32356             diff = v - m;
32357         }else if(v - diff > mx){
32358             diff = mx - v;
32359         }
32360         return diff;
32361     },
32362
32363     // private
32364     onMouseMove : function(e){
32365         
32366         if(this.enabled){
32367             try{// try catch so if something goes wrong the user doesn't get hung
32368
32369             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32370                 return;
32371             }
32372
32373             //var curXY = this.startPoint;
32374             var curSize = this.curSize || this.startBox;
32375             var x = this.startBox.x, y = this.startBox.y;
32376             var ox = x, oy = y;
32377             var w = curSize.width, h = curSize.height;
32378             var ow = w, oh = h;
32379             var mw = this.minWidth, mh = this.minHeight;
32380             var mxw = this.maxWidth, mxh = this.maxHeight;
32381             var wi = this.widthIncrement;
32382             var hi = this.heightIncrement;
32383
32384             var eventXY = e.getXY();
32385             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32386             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32387
32388             var pos = this.activeHandle.position;
32389
32390             switch(pos){
32391                 case "east":
32392                     w += diffX;
32393                     w = Math.min(Math.max(mw, w), mxw);
32394                     break;
32395              
32396                 case "south":
32397                     h += diffY;
32398                     h = Math.min(Math.max(mh, h), mxh);
32399                     break;
32400                 case "southeast":
32401                     w += diffX;
32402                     h += diffY;
32403                     w = Math.min(Math.max(mw, w), mxw);
32404                     h = Math.min(Math.max(mh, h), mxh);
32405                     break;
32406                 case "north":
32407                     diffY = this.constrain(h, diffY, mh, mxh);
32408                     y += diffY;
32409                     h -= diffY;
32410                     break;
32411                 case "hdrag":
32412                     
32413                     if (wi) {
32414                         var adiffX = Math.abs(diffX);
32415                         var sub = (adiffX % wi); // how much 
32416                         if (sub > (wi/2)) { // far enough to snap
32417                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32418                         } else {
32419                             // remove difference.. 
32420                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32421                         }
32422                     }
32423                     x += diffX;
32424                     x = Math.max(this.minX, x);
32425                     break;
32426                 case "west":
32427                     diffX = this.constrain(w, diffX, mw, mxw);
32428                     x += diffX;
32429                     w -= diffX;
32430                     break;
32431                 case "northeast":
32432                     w += diffX;
32433                     w = Math.min(Math.max(mw, w), mxw);
32434                     diffY = this.constrain(h, diffY, mh, mxh);
32435                     y += diffY;
32436                     h -= diffY;
32437                     break;
32438                 case "northwest":
32439                     diffX = this.constrain(w, diffX, mw, mxw);
32440                     diffY = this.constrain(h, diffY, mh, mxh);
32441                     y += diffY;
32442                     h -= diffY;
32443                     x += diffX;
32444                     w -= diffX;
32445                     break;
32446                case "southwest":
32447                     diffX = this.constrain(w, diffX, mw, mxw);
32448                     h += diffY;
32449                     h = Math.min(Math.max(mh, h), mxh);
32450                     x += diffX;
32451                     w -= diffX;
32452                     break;
32453             }
32454
32455             var sw = this.snap(w, wi, mw);
32456             var sh = this.snap(h, hi, mh);
32457             if(sw != w || sh != h){
32458                 switch(pos){
32459                     case "northeast":
32460                         y -= sh - h;
32461                     break;
32462                     case "north":
32463                         y -= sh - h;
32464                         break;
32465                     case "southwest":
32466                         x -= sw - w;
32467                     break;
32468                     case "west":
32469                         x -= sw - w;
32470                         break;
32471                     case "northwest":
32472                         x -= sw - w;
32473                         y -= sh - h;
32474                     break;
32475                 }
32476                 w = sw;
32477                 h = sh;
32478             }
32479
32480             if(this.preserveRatio){
32481                 switch(pos){
32482                     case "southeast":
32483                     case "east":
32484                         h = oh * (w/ow);
32485                         h = Math.min(Math.max(mh, h), mxh);
32486                         w = ow * (h/oh);
32487                        break;
32488                     case "south":
32489                         w = ow * (h/oh);
32490                         w = Math.min(Math.max(mw, w), mxw);
32491                         h = oh * (w/ow);
32492                         break;
32493                     case "northeast":
32494                         w = ow * (h/oh);
32495                         w = Math.min(Math.max(mw, w), mxw);
32496                         h = oh * (w/ow);
32497                     break;
32498                     case "north":
32499                         var tw = w;
32500                         w = ow * (h/oh);
32501                         w = Math.min(Math.max(mw, w), mxw);
32502                         h = oh * (w/ow);
32503                         x += (tw - w) / 2;
32504                         break;
32505                     case "southwest":
32506                         h = oh * (w/ow);
32507                         h = Math.min(Math.max(mh, h), mxh);
32508                         var tw = w;
32509                         w = ow * (h/oh);
32510                         x += tw - w;
32511                         break;
32512                     case "west":
32513                         var th = h;
32514                         h = oh * (w/ow);
32515                         h = Math.min(Math.max(mh, h), mxh);
32516                         y += (th - h) / 2;
32517                         var tw = w;
32518                         w = ow * (h/oh);
32519                         x += tw - w;
32520                        break;
32521                     case "northwest":
32522                         var tw = w;
32523                         var th = h;
32524                         h = oh * (w/ow);
32525                         h = Math.min(Math.max(mh, h), mxh);
32526                         w = ow * (h/oh);
32527                         y += th - h;
32528                         x += tw - w;
32529                        break;
32530
32531                 }
32532             }
32533             if (pos == 'hdrag') {
32534                 w = ow;
32535             }
32536             this.proxy.setBounds(x, y, w, h);
32537             if(this.dynamic){
32538                 this.resizeElement();
32539             }
32540             }catch(e){}
32541         }
32542         this.fireEvent("resizing", this, x, y, w, h, e);
32543     },
32544
32545     // private
32546     handleOver : function(){
32547         if(this.enabled){
32548             this.el.addClass("x-resizable-over");
32549         }
32550     },
32551
32552     // private
32553     handleOut : function(){
32554         if(!this.resizing){
32555             this.el.removeClass("x-resizable-over");
32556         }
32557     },
32558
32559     /**
32560      * Returns the element this component is bound to.
32561      * @return {Roo.Element}
32562      */
32563     getEl : function(){
32564         return this.el;
32565     },
32566
32567     /**
32568      * Returns the resizeChild element (or null).
32569      * @return {Roo.Element}
32570      */
32571     getResizeChild : function(){
32572         return this.resizeChild;
32573     },
32574     groupHandler : function()
32575     {
32576         
32577     },
32578     /**
32579      * Destroys this resizable. If the element was wrapped and
32580      * removeEl is not true then the element remains.
32581      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32582      */
32583     destroy : function(removeEl){
32584         this.proxy.remove();
32585         if(this.overlay){
32586             this.overlay.removeAllListeners();
32587             this.overlay.remove();
32588         }
32589         var ps = Roo.Resizable.positions;
32590         for(var k in ps){
32591             if(typeof ps[k] != "function" && this[ps[k]]){
32592                 var h = this[ps[k]];
32593                 h.el.removeAllListeners();
32594                 h.el.remove();
32595             }
32596         }
32597         if(removeEl){
32598             this.el.update("");
32599             this.el.remove();
32600         }
32601     }
32602 });
32603
32604 // private
32605 // hash to map config positions to true positions
32606 Roo.Resizable.positions = {
32607     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32608     hd: "hdrag"
32609 };
32610
32611 // private
32612 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32613     if(!this.tpl){
32614         // only initialize the template if resizable is used
32615         var tpl = Roo.DomHelper.createTemplate(
32616             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32617         );
32618         tpl.compile();
32619         Roo.Resizable.Handle.prototype.tpl = tpl;
32620     }
32621     this.position = pos;
32622     this.rz = rz;
32623     // show north drag fro topdra
32624     var handlepos = pos == 'hdrag' ? 'north' : pos;
32625     
32626     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32627     if (pos == 'hdrag') {
32628         this.el.setStyle('cursor', 'pointer');
32629     }
32630     this.el.unselectable();
32631     if(transparent){
32632         this.el.setOpacity(0);
32633     }
32634     this.el.on("mousedown", this.onMouseDown, this);
32635     if(!disableTrackOver){
32636         this.el.on("mouseover", this.onMouseOver, this);
32637         this.el.on("mouseout", this.onMouseOut, this);
32638     }
32639 };
32640
32641 // private
32642 Roo.Resizable.Handle.prototype = {
32643     afterResize : function(rz){
32644         Roo.log('after?');
32645         // do nothing
32646     },
32647     // private
32648     onMouseDown : function(e){
32649         this.rz.onMouseDown(this, e);
32650     },
32651     // private
32652     onMouseOver : function(e){
32653         this.rz.handleOver(this, e);
32654     },
32655     // private
32656     onMouseOut : function(e){
32657         this.rz.handleOut(this, e);
32658     }
32659 };/*
32660  * Based on:
32661  * Ext JS Library 1.1.1
32662  * Copyright(c) 2006-2007, Ext JS, LLC.
32663  *
32664  * Originally Released Under LGPL - original licence link has changed is not relivant.
32665  *
32666  * Fork - LGPL
32667  * <script type="text/javascript">
32668  */
32669
32670 /**
32671  * @class Roo.Editor
32672  * @extends Roo.Component
32673  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32674  * @constructor
32675  * Create a new Editor
32676  * @param {Roo.form.Field} field The Field object (or descendant)
32677  * @param {Object} config The config object
32678  */
32679 Roo.Editor = function(field, config){
32680     Roo.Editor.superclass.constructor.call(this, config);
32681     this.field = field;
32682     this.addEvents({
32683         /**
32684              * @event beforestartedit
32685              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32686              * false from the handler of this event.
32687              * @param {Editor} this
32688              * @param {Roo.Element} boundEl The underlying element bound to this editor
32689              * @param {Mixed} value The field value being set
32690              */
32691         "beforestartedit" : true,
32692         /**
32693              * @event startedit
32694              * Fires when this editor is displayed
32695              * @param {Roo.Element} boundEl The underlying element bound to this editor
32696              * @param {Mixed} value The starting field value
32697              */
32698         "startedit" : true,
32699         /**
32700              * @event beforecomplete
32701              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32702              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32703              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32704              * event will not fire since no edit actually occurred.
32705              * @param {Editor} this
32706              * @param {Mixed} value The current field value
32707              * @param {Mixed} startValue The original field value
32708              */
32709         "beforecomplete" : true,
32710         /**
32711              * @event complete
32712              * Fires after editing is complete and any changed value has been written to the underlying field.
32713              * @param {Editor} this
32714              * @param {Mixed} value The current field value
32715              * @param {Mixed} startValue The original field value
32716              */
32717         "complete" : true,
32718         /**
32719          * @event specialkey
32720          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32721          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32722          * @param {Roo.form.Field} this
32723          * @param {Roo.EventObject} e The event object
32724          */
32725         "specialkey" : true
32726     });
32727 };
32728
32729 Roo.extend(Roo.Editor, Roo.Component, {
32730     /**
32731      * @cfg {Boolean/String} autosize
32732      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32733      * or "height" to adopt the height only (defaults to false)
32734      */
32735     /**
32736      * @cfg {Boolean} revertInvalid
32737      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32738      * validation fails (defaults to true)
32739      */
32740     /**
32741      * @cfg {Boolean} ignoreNoChange
32742      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32743      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32744      * will never be ignored.
32745      */
32746     /**
32747      * @cfg {Boolean} hideEl
32748      * False to keep the bound element visible while the editor is displayed (defaults to true)
32749      */
32750     /**
32751      * @cfg {Mixed} value
32752      * The data value of the underlying field (defaults to "")
32753      */
32754     value : "",
32755     /**
32756      * @cfg {String} alignment
32757      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32758      */
32759     alignment: "c-c?",
32760     /**
32761      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32762      * for bottom-right shadow (defaults to "frame")
32763      */
32764     shadow : "frame",
32765     /**
32766      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32767      */
32768     constrain : false,
32769     /**
32770      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32771      */
32772     completeOnEnter : false,
32773     /**
32774      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32775      */
32776     cancelOnEsc : false,
32777     /**
32778      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32779      */
32780     updateEl : false,
32781
32782     // private
32783     onRender : function(ct, position){
32784         this.el = new Roo.Layer({
32785             shadow: this.shadow,
32786             cls: "x-editor",
32787             parentEl : ct,
32788             shim : this.shim,
32789             shadowOffset:4,
32790             id: this.id,
32791             constrain: this.constrain
32792         });
32793         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32794         if(this.field.msgTarget != 'title'){
32795             this.field.msgTarget = 'qtip';
32796         }
32797         this.field.render(this.el);
32798         if(Roo.isGecko){
32799             this.field.el.dom.setAttribute('autocomplete', 'off');
32800         }
32801         this.field.on("specialkey", this.onSpecialKey, this);
32802         if(this.swallowKeys){
32803             this.field.el.swallowEvent(['keydown','keypress']);
32804         }
32805         this.field.show();
32806         this.field.on("blur", this.onBlur, this);
32807         if(this.field.grow){
32808             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32809         }
32810     },
32811
32812     onSpecialKey : function(field, e)
32813     {
32814         //Roo.log('editor onSpecialKey');
32815         if(this.completeOnEnter && e.getKey() == e.ENTER){
32816             e.stopEvent();
32817             this.completeEdit();
32818             return;
32819         }
32820         // do not fire special key otherwise it might hide close the editor...
32821         if(e.getKey() == e.ENTER){    
32822             return;
32823         }
32824         if(this.cancelOnEsc && e.getKey() == e.ESC){
32825             this.cancelEdit();
32826             return;
32827         } 
32828         this.fireEvent('specialkey', field, e);
32829     
32830     },
32831
32832     /**
32833      * Starts the editing process and shows the editor.
32834      * @param {String/HTMLElement/Element} el The element to edit
32835      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32836       * to the innerHTML of el.
32837      */
32838     startEdit : function(el, value){
32839         if(this.editing){
32840             this.completeEdit();
32841         }
32842         this.boundEl = Roo.get(el);
32843         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32844         if(!this.rendered){
32845             this.render(this.parentEl || document.body);
32846         }
32847         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32848             return;
32849         }
32850         this.startValue = v;
32851         this.field.setValue(v);
32852         if(this.autoSize){
32853             var sz = this.boundEl.getSize();
32854             switch(this.autoSize){
32855                 case "width":
32856                 this.setSize(sz.width,  "");
32857                 break;
32858                 case "height":
32859                 this.setSize("",  sz.height);
32860                 break;
32861                 default:
32862                 this.setSize(sz.width,  sz.height);
32863             }
32864         }
32865         this.el.alignTo(this.boundEl, this.alignment);
32866         this.editing = true;
32867         if(Roo.QuickTips){
32868             Roo.QuickTips.disable();
32869         }
32870         this.show();
32871     },
32872
32873     /**
32874      * Sets the height and width of this editor.
32875      * @param {Number} width The new width
32876      * @param {Number} height The new height
32877      */
32878     setSize : function(w, h){
32879         this.field.setSize(w, h);
32880         if(this.el){
32881             this.el.sync();
32882         }
32883     },
32884
32885     /**
32886      * Realigns the editor to the bound field based on the current alignment config value.
32887      */
32888     realign : function(){
32889         this.el.alignTo(this.boundEl, this.alignment);
32890     },
32891
32892     /**
32893      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32894      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32895      */
32896     completeEdit : function(remainVisible){
32897         if(!this.editing){
32898             return;
32899         }
32900         var v = this.getValue();
32901         if(this.revertInvalid !== false && !this.field.isValid()){
32902             v = this.startValue;
32903             this.cancelEdit(true);
32904         }
32905         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32906             this.editing = false;
32907             this.hide();
32908             return;
32909         }
32910         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32911             this.editing = false;
32912             if(this.updateEl && this.boundEl){
32913                 this.boundEl.update(v);
32914             }
32915             if(remainVisible !== true){
32916                 this.hide();
32917             }
32918             this.fireEvent("complete", this, v, this.startValue);
32919         }
32920     },
32921
32922     // private
32923     onShow : function(){
32924         this.el.show();
32925         if(this.hideEl !== false){
32926             this.boundEl.hide();
32927         }
32928         this.field.show();
32929         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32930             this.fixIEFocus = true;
32931             this.deferredFocus.defer(50, this);
32932         }else{
32933             this.field.focus();
32934         }
32935         this.fireEvent("startedit", this.boundEl, this.startValue);
32936     },
32937
32938     deferredFocus : function(){
32939         if(this.editing){
32940             this.field.focus();
32941         }
32942     },
32943
32944     /**
32945      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32946      * reverted to the original starting value.
32947      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32948      * cancel (defaults to false)
32949      */
32950     cancelEdit : function(remainVisible){
32951         if(this.editing){
32952             this.setValue(this.startValue);
32953             if(remainVisible !== true){
32954                 this.hide();
32955             }
32956         }
32957     },
32958
32959     // private
32960     onBlur : function(){
32961         if(this.allowBlur !== true && this.editing){
32962             this.completeEdit();
32963         }
32964     },
32965
32966     // private
32967     onHide : function(){
32968         if(this.editing){
32969             this.completeEdit();
32970             return;
32971         }
32972         this.field.blur();
32973         if(this.field.collapse){
32974             this.field.collapse();
32975         }
32976         this.el.hide();
32977         if(this.hideEl !== false){
32978             this.boundEl.show();
32979         }
32980         if(Roo.QuickTips){
32981             Roo.QuickTips.enable();
32982         }
32983     },
32984
32985     /**
32986      * Sets the data value of the editor
32987      * @param {Mixed} value Any valid value supported by the underlying field
32988      */
32989     setValue : function(v){
32990         this.field.setValue(v);
32991     },
32992
32993     /**
32994      * Gets the data value of the editor
32995      * @return {Mixed} The data value
32996      */
32997     getValue : function(){
32998         return this.field.getValue();
32999     }
33000 });/*
33001  * Based on:
33002  * Ext JS Library 1.1.1
33003  * Copyright(c) 2006-2007, Ext JS, LLC.
33004  *
33005  * Originally Released Under LGPL - original licence link has changed is not relivant.
33006  *
33007  * Fork - LGPL
33008  * <script type="text/javascript">
33009  */
33010  
33011 /**
33012  * @class Roo.BasicDialog
33013  * @extends Roo.util.Observable
33014  * @parent none builder
33015  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33016  * <pre><code>
33017 var dlg = new Roo.BasicDialog("my-dlg", {
33018     height: 200,
33019     width: 300,
33020     minHeight: 100,
33021     minWidth: 150,
33022     modal: true,
33023     proxyDrag: true,
33024     shadow: true
33025 });
33026 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33027 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33028 dlg.addButton('Cancel', dlg.hide, dlg);
33029 dlg.show();
33030 </code></pre>
33031   <b>A Dialog should always be a direct child of the body element.</b>
33032  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33033  * @cfg {String} title Default text to display in the title bar (defaults to null)
33034  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33035  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33036  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33037  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33038  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33039  * (defaults to null with no animation)
33040  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33041  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33042  * property for valid values (defaults to 'all')
33043  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33044  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33045  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33046  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33047  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33048  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33049  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33050  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33051  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33052  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33053  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33054  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33055  * draggable = true (defaults to false)
33056  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33057  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33058  * shadow (defaults to false)
33059  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33060  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33061  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33062  * @cfg {Array} buttons Array of buttons
33063  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33064  * @constructor
33065  * Create a new BasicDialog.
33066  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33067  * @param {Object} config Configuration options
33068  */
33069 Roo.BasicDialog = function(el, config){
33070     this.el = Roo.get(el);
33071     var dh = Roo.DomHelper;
33072     if(!this.el && config && config.autoCreate){
33073         if(typeof config.autoCreate == "object"){
33074             if(!config.autoCreate.id){
33075                 config.autoCreate.id = el;
33076             }
33077             this.el = dh.append(document.body,
33078                         config.autoCreate, true);
33079         }else{
33080             this.el = dh.append(document.body,
33081                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33082         }
33083     }
33084     el = this.el;
33085     el.setDisplayed(true);
33086     el.hide = this.hideAction;
33087     this.id = el.id;
33088     el.addClass("x-dlg");
33089
33090     Roo.apply(this, config);
33091
33092     this.proxy = el.createProxy("x-dlg-proxy");
33093     this.proxy.hide = this.hideAction;
33094     this.proxy.setOpacity(.5);
33095     this.proxy.hide();
33096
33097     if(config.width){
33098         el.setWidth(config.width);
33099     }
33100     if(config.height){
33101         el.setHeight(config.height);
33102     }
33103     this.size = el.getSize();
33104     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33105         this.xy = [config.x,config.y];
33106     }else{
33107         this.xy = el.getCenterXY(true);
33108     }
33109     /** The header element @type Roo.Element */
33110     this.header = el.child("> .x-dlg-hd");
33111     /** The body element @type Roo.Element */
33112     this.body = el.child("> .x-dlg-bd");
33113     /** The footer element @type Roo.Element */
33114     this.footer = el.child("> .x-dlg-ft");
33115
33116     if(!this.header){
33117         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33118     }
33119     if(!this.body){
33120         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33121     }
33122
33123     this.header.unselectable();
33124     if(this.title){
33125         this.header.update(this.title);
33126     }
33127     // this element allows the dialog to be focused for keyboard event
33128     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33129     this.focusEl.swallowEvent("click", true);
33130
33131     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33132
33133     // wrap the body and footer for special rendering
33134     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33135     if(this.footer){
33136         this.bwrap.dom.appendChild(this.footer.dom);
33137     }
33138
33139     this.bg = this.el.createChild({
33140         tag: "div", cls:"x-dlg-bg",
33141         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33142     });
33143     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33144
33145
33146     if(this.autoScroll !== false && !this.autoTabs){
33147         this.body.setStyle("overflow", "auto");
33148     }
33149
33150     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33151
33152     if(this.closable !== false){
33153         this.el.addClass("x-dlg-closable");
33154         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33155         this.close.on("click", this.closeClick, this);
33156         this.close.addClassOnOver("x-dlg-close-over");
33157     }
33158     if(this.collapsible !== false){
33159         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33160         this.collapseBtn.on("click", this.collapseClick, this);
33161         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33162         this.header.on("dblclick", this.collapseClick, this);
33163     }
33164     if(this.resizable !== false){
33165         this.el.addClass("x-dlg-resizable");
33166         this.resizer = new Roo.Resizable(el, {
33167             minWidth: this.minWidth || 80,
33168             minHeight:this.minHeight || 80,
33169             handles: this.resizeHandles || "all",
33170             pinned: true
33171         });
33172         this.resizer.on("beforeresize", this.beforeResize, this);
33173         this.resizer.on("resize", this.onResize, this);
33174     }
33175     if(this.draggable !== false){
33176         el.addClass("x-dlg-draggable");
33177         if (!this.proxyDrag) {
33178             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33179         }
33180         else {
33181             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33182         }
33183         dd.setHandleElId(this.header.id);
33184         dd.endDrag = this.endMove.createDelegate(this);
33185         dd.startDrag = this.startMove.createDelegate(this);
33186         dd.onDrag = this.onDrag.createDelegate(this);
33187         dd.scroll = false;
33188         this.dd = dd;
33189     }
33190     if(this.modal){
33191         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33192         this.mask.enableDisplayMode("block");
33193         this.mask.hide();
33194         this.el.addClass("x-dlg-modal");
33195     }
33196     if(this.shadow){
33197         this.shadow = new Roo.Shadow({
33198             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33199             offset : this.shadowOffset
33200         });
33201     }else{
33202         this.shadowOffset = 0;
33203     }
33204     if(Roo.useShims && this.shim !== false){
33205         this.shim = this.el.createShim();
33206         this.shim.hide = this.hideAction;
33207         this.shim.hide();
33208     }else{
33209         this.shim = false;
33210     }
33211     if(this.autoTabs){
33212         this.initTabs();
33213     }
33214     if (this.buttons) { 
33215         var bts= this.buttons;
33216         this.buttons = [];
33217         Roo.each(bts, function(b) {
33218             this.addButton(b);
33219         }, this);
33220     }
33221     
33222     
33223     this.addEvents({
33224         /**
33225          * @event keydown
33226          * Fires when a key is pressed
33227          * @param {Roo.BasicDialog} this
33228          * @param {Roo.EventObject} e
33229          */
33230         "keydown" : true,
33231         /**
33232          * @event move
33233          * Fires when this dialog is moved by the user.
33234          * @param {Roo.BasicDialog} this
33235          * @param {Number} x The new page X
33236          * @param {Number} y The new page Y
33237          */
33238         "move" : true,
33239         /**
33240          * @event resize
33241          * Fires when this dialog is resized by the user.
33242          * @param {Roo.BasicDialog} this
33243          * @param {Number} width The new width
33244          * @param {Number} height The new height
33245          */
33246         "resize" : true,
33247         /**
33248          * @event beforehide
33249          * Fires before this dialog is hidden.
33250          * @param {Roo.BasicDialog} this
33251          */
33252         "beforehide" : true,
33253         /**
33254          * @event hide
33255          * Fires when this dialog is hidden.
33256          * @param {Roo.BasicDialog} this
33257          */
33258         "hide" : true,
33259         /**
33260          * @event beforeshow
33261          * Fires before this dialog is shown.
33262          * @param {Roo.BasicDialog} this
33263          */
33264         "beforeshow" : true,
33265         /**
33266          * @event show
33267          * Fires when this dialog is shown.
33268          * @param {Roo.BasicDialog} this
33269          */
33270         "show" : true
33271     });
33272     el.on("keydown", this.onKeyDown, this);
33273     el.on("mousedown", this.toFront, this);
33274     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33275     this.el.hide();
33276     Roo.DialogManager.register(this);
33277     Roo.BasicDialog.superclass.constructor.call(this);
33278 };
33279
33280 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33281     shadowOffset: Roo.isIE ? 6 : 5,
33282     minHeight: 80,
33283     minWidth: 200,
33284     minButtonWidth: 75,
33285     defaultButton: null,
33286     buttonAlign: "right",
33287     tabTag: 'div',
33288     firstShow: true,
33289
33290     /**
33291      * Sets the dialog title text
33292      * @param {String} text The title text to display
33293      * @return {Roo.BasicDialog} this
33294      */
33295     setTitle : function(text){
33296         this.header.update(text);
33297         return this;
33298     },
33299
33300     // private
33301     closeClick : function(){
33302         this.hide();
33303     },
33304
33305     // private
33306     collapseClick : function(){
33307         this[this.collapsed ? "expand" : "collapse"]();
33308     },
33309
33310     /**
33311      * Collapses the dialog to its minimized state (only the title bar is visible).
33312      * Equivalent to the user clicking the collapse dialog button.
33313      */
33314     collapse : function(){
33315         if(!this.collapsed){
33316             this.collapsed = true;
33317             this.el.addClass("x-dlg-collapsed");
33318             this.restoreHeight = this.el.getHeight();
33319             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33320         }
33321     },
33322
33323     /**
33324      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33325      * clicking the expand dialog button.
33326      */
33327     expand : function(){
33328         if(this.collapsed){
33329             this.collapsed = false;
33330             this.el.removeClass("x-dlg-collapsed");
33331             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33332         }
33333     },
33334
33335     /**
33336      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33337      * @return {Roo.TabPanel} The tabs component
33338      */
33339     initTabs : function(){
33340         var tabs = this.getTabs();
33341         while(tabs.getTab(0)){
33342             tabs.removeTab(0);
33343         }
33344         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33345             var dom = el.dom;
33346             tabs.addTab(Roo.id(dom), dom.title);
33347             dom.title = "";
33348         });
33349         tabs.activate(0);
33350         return tabs;
33351     },
33352
33353     // private
33354     beforeResize : function(){
33355         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33356     },
33357
33358     // private
33359     onResize : function(){
33360         this.refreshSize();
33361         this.syncBodyHeight();
33362         this.adjustAssets();
33363         this.focus();
33364         this.fireEvent("resize", this, this.size.width, this.size.height);
33365     },
33366
33367     // private
33368     onKeyDown : function(e){
33369         if(this.isVisible()){
33370             this.fireEvent("keydown", this, e);
33371         }
33372     },
33373
33374     /**
33375      * Resizes the dialog.
33376      * @param {Number} width
33377      * @param {Number} height
33378      * @return {Roo.BasicDialog} this
33379      */
33380     resizeTo : function(width, height){
33381         this.el.setSize(width, height);
33382         this.size = {width: width, height: height};
33383         this.syncBodyHeight();
33384         if(this.fixedcenter){
33385             this.center();
33386         }
33387         if(this.isVisible()){
33388             this.constrainXY();
33389             this.adjustAssets();
33390         }
33391         this.fireEvent("resize", this, width, height);
33392         return this;
33393     },
33394
33395
33396     /**
33397      * Resizes the dialog to fit the specified content size.
33398      * @param {Number} width
33399      * @param {Number} height
33400      * @return {Roo.BasicDialog} this
33401      */
33402     setContentSize : function(w, h){
33403         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33404         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33405         //if(!this.el.isBorderBox()){
33406             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33407             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33408         //}
33409         if(this.tabs){
33410             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33411             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33412         }
33413         this.resizeTo(w, h);
33414         return this;
33415     },
33416
33417     /**
33418      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33419      * executed in response to a particular key being pressed while the dialog is active.
33420      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33421      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33422      * @param {Function} fn The function to call
33423      * @param {Object} scope (optional) The scope of the function
33424      * @return {Roo.BasicDialog} this
33425      */
33426     addKeyListener : function(key, fn, scope){
33427         var keyCode, shift, ctrl, alt;
33428         if(typeof key == "object" && !(key instanceof Array)){
33429             keyCode = key["key"];
33430             shift = key["shift"];
33431             ctrl = key["ctrl"];
33432             alt = key["alt"];
33433         }else{
33434             keyCode = key;
33435         }
33436         var handler = function(dlg, e){
33437             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33438                 var k = e.getKey();
33439                 if(keyCode instanceof Array){
33440                     for(var i = 0, len = keyCode.length; i < len; i++){
33441                         if(keyCode[i] == k){
33442                           fn.call(scope || window, dlg, k, e);
33443                           return;
33444                         }
33445                     }
33446                 }else{
33447                     if(k == keyCode){
33448                         fn.call(scope || window, dlg, k, e);
33449                     }
33450                 }
33451             }
33452         };
33453         this.on("keydown", handler);
33454         return this;
33455     },
33456
33457     /**
33458      * Returns the TabPanel component (creates it if it doesn't exist).
33459      * Note: If you wish to simply check for the existence of tabs without creating them,
33460      * check for a null 'tabs' property.
33461      * @return {Roo.TabPanel} The tabs component
33462      */
33463     getTabs : function(){
33464         if(!this.tabs){
33465             this.el.addClass("x-dlg-auto-tabs");
33466             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33467             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33468         }
33469         return this.tabs;
33470     },
33471
33472     /**
33473      * Adds a button to the footer section of the dialog.
33474      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33475      * object or a valid Roo.DomHelper element config
33476      * @param {Function} handler The function called when the button is clicked
33477      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33478      * @return {Roo.Button} The new button
33479      */
33480     addButton : function(config, handler, scope){
33481         var dh = Roo.DomHelper;
33482         if(!this.footer){
33483             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33484         }
33485         if(!this.btnContainer){
33486             var tb = this.footer.createChild({
33487
33488                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33489                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33490             }, null, true);
33491             this.btnContainer = tb.firstChild.firstChild.firstChild;
33492         }
33493         var bconfig = {
33494             handler: handler,
33495             scope: scope,
33496             minWidth: this.minButtonWidth,
33497             hideParent:true
33498         };
33499         if(typeof config == "string"){
33500             bconfig.text = config;
33501         }else{
33502             if(config.tag){
33503                 bconfig.dhconfig = config;
33504             }else{
33505                 Roo.apply(bconfig, config);
33506             }
33507         }
33508         var fc = false;
33509         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33510             bconfig.position = Math.max(0, bconfig.position);
33511             fc = this.btnContainer.childNodes[bconfig.position];
33512         }
33513          
33514         var btn = new Roo.Button(
33515             fc ? 
33516                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33517                 : this.btnContainer.appendChild(document.createElement("td")),
33518             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33519             bconfig
33520         );
33521         this.syncBodyHeight();
33522         if(!this.buttons){
33523             /**
33524              * Array of all the buttons that have been added to this dialog via addButton
33525              * @type Array
33526              */
33527             this.buttons = [];
33528         }
33529         this.buttons.push(btn);
33530         return btn;
33531     },
33532
33533     /**
33534      * Sets the default button to be focused when the dialog is displayed.
33535      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33536      * @return {Roo.BasicDialog} this
33537      */
33538     setDefaultButton : function(btn){
33539         this.defaultButton = btn;
33540         return this;
33541     },
33542
33543     // private
33544     getHeaderFooterHeight : function(safe){
33545         var height = 0;
33546         if(this.header){
33547            height += this.header.getHeight();
33548         }
33549         if(this.footer){
33550            var fm = this.footer.getMargins();
33551             height += (this.footer.getHeight()+fm.top+fm.bottom);
33552         }
33553         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33554         height += this.centerBg.getPadding("tb");
33555         return height;
33556     },
33557
33558     // private
33559     syncBodyHeight : function()
33560     {
33561         var bd = this.body, // the text
33562             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33563             bw = this.bwrap;
33564         var height = this.size.height - this.getHeaderFooterHeight(false);
33565         bd.setHeight(height-bd.getMargins("tb"));
33566         var hh = this.header.getHeight();
33567         var h = this.size.height-hh;
33568         cb.setHeight(h);
33569         
33570         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33571         bw.setHeight(h-cb.getPadding("tb"));
33572         
33573         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33574         bd.setWidth(bw.getWidth(true));
33575         if(this.tabs){
33576             this.tabs.syncHeight();
33577             if(Roo.isIE){
33578                 this.tabs.el.repaint();
33579             }
33580         }
33581     },
33582
33583     /**
33584      * Restores the previous state of the dialog if Roo.state is configured.
33585      * @return {Roo.BasicDialog} this
33586      */
33587     restoreState : function(){
33588         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33589         if(box && box.width){
33590             this.xy = [box.x, box.y];
33591             this.resizeTo(box.width, box.height);
33592         }
33593         return this;
33594     },
33595
33596     // private
33597     beforeShow : function(){
33598         this.expand();
33599         if(this.fixedcenter){
33600             this.xy = this.el.getCenterXY(true);
33601         }
33602         if(this.modal){
33603             Roo.get(document.body).addClass("x-body-masked");
33604             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33605             this.mask.show();
33606         }
33607         this.constrainXY();
33608     },
33609
33610     // private
33611     animShow : function(){
33612         var b = Roo.get(this.animateTarget).getBox();
33613         this.proxy.setSize(b.width, b.height);
33614         this.proxy.setLocation(b.x, b.y);
33615         this.proxy.show();
33616         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33617                     true, .35, this.showEl.createDelegate(this));
33618     },
33619
33620     /**
33621      * Shows the dialog.
33622      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33623      * @return {Roo.BasicDialog} this
33624      */
33625     show : function(animateTarget){
33626         if (this.fireEvent("beforeshow", this) === false){
33627             return;
33628         }
33629         if(this.syncHeightBeforeShow){
33630             this.syncBodyHeight();
33631         }else if(this.firstShow){
33632             this.firstShow = false;
33633             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33634         }
33635         this.animateTarget = animateTarget || this.animateTarget;
33636         if(!this.el.isVisible()){
33637             this.beforeShow();
33638             if(this.animateTarget && Roo.get(this.animateTarget)){
33639                 this.animShow();
33640             }else{
33641                 this.showEl();
33642             }
33643         }
33644         return this;
33645     },
33646
33647     // private
33648     showEl : function(){
33649         this.proxy.hide();
33650         this.el.setXY(this.xy);
33651         this.el.show();
33652         this.adjustAssets(true);
33653         this.toFront();
33654         this.focus();
33655         // IE peekaboo bug - fix found by Dave Fenwick
33656         if(Roo.isIE){
33657             this.el.repaint();
33658         }
33659         this.fireEvent("show", this);
33660     },
33661
33662     /**
33663      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33664      * dialog itself will receive focus.
33665      */
33666     focus : function(){
33667         if(this.defaultButton){
33668             this.defaultButton.focus();
33669         }else{
33670             this.focusEl.focus();
33671         }
33672     },
33673
33674     // private
33675     constrainXY : function(){
33676         if(this.constraintoviewport !== false){
33677             if(!this.viewSize){
33678                 if(this.container){
33679                     var s = this.container.getSize();
33680                     this.viewSize = [s.width, s.height];
33681                 }else{
33682                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33683                 }
33684             }
33685             var s = Roo.get(this.container||document).getScroll();
33686
33687             var x = this.xy[0], y = this.xy[1];
33688             var w = this.size.width, h = this.size.height;
33689             var vw = this.viewSize[0], vh = this.viewSize[1];
33690             // only move it if it needs it
33691             var moved = false;
33692             // first validate right/bottom
33693             if(x + w > vw+s.left){
33694                 x = vw - w;
33695                 moved = true;
33696             }
33697             if(y + h > vh+s.top){
33698                 y = vh - h;
33699                 moved = true;
33700             }
33701             // then make sure top/left isn't negative
33702             if(x < s.left){
33703                 x = s.left;
33704                 moved = true;
33705             }
33706             if(y < s.top){
33707                 y = s.top;
33708                 moved = true;
33709             }
33710             if(moved){
33711                 // cache xy
33712                 this.xy = [x, y];
33713                 if(this.isVisible()){
33714                     this.el.setLocation(x, y);
33715                     this.adjustAssets();
33716                 }
33717             }
33718         }
33719     },
33720
33721     // private
33722     onDrag : function(){
33723         if(!this.proxyDrag){
33724             this.xy = this.el.getXY();
33725             this.adjustAssets();
33726         }
33727     },
33728
33729     // private
33730     adjustAssets : function(doShow){
33731         var x = this.xy[0], y = this.xy[1];
33732         var w = this.size.width, h = this.size.height;
33733         if(doShow === true){
33734             if(this.shadow){
33735                 this.shadow.show(this.el);
33736             }
33737             if(this.shim){
33738                 this.shim.show();
33739             }
33740         }
33741         if(this.shadow && this.shadow.isVisible()){
33742             this.shadow.show(this.el);
33743         }
33744         if(this.shim && this.shim.isVisible()){
33745             this.shim.setBounds(x, y, w, h);
33746         }
33747     },
33748
33749     // private
33750     adjustViewport : function(w, h){
33751         if(!w || !h){
33752             w = Roo.lib.Dom.getViewWidth();
33753             h = Roo.lib.Dom.getViewHeight();
33754         }
33755         // cache the size
33756         this.viewSize = [w, h];
33757         if(this.modal && this.mask.isVisible()){
33758             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33759             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33760         }
33761         if(this.isVisible()){
33762             this.constrainXY();
33763         }
33764     },
33765
33766     /**
33767      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33768      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33769      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33770      */
33771     destroy : function(removeEl){
33772         if(this.isVisible()){
33773             this.animateTarget = null;
33774             this.hide();
33775         }
33776         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33777         if(this.tabs){
33778             this.tabs.destroy(removeEl);
33779         }
33780         Roo.destroy(
33781              this.shim,
33782              this.proxy,
33783              this.resizer,
33784              this.close,
33785              this.mask
33786         );
33787         if(this.dd){
33788             this.dd.unreg();
33789         }
33790         if(this.buttons){
33791            for(var i = 0, len = this.buttons.length; i < len; i++){
33792                this.buttons[i].destroy();
33793            }
33794         }
33795         this.el.removeAllListeners();
33796         if(removeEl === true){
33797             this.el.update("");
33798             this.el.remove();
33799         }
33800         Roo.DialogManager.unregister(this);
33801     },
33802
33803     // private
33804     startMove : function(){
33805         if(this.proxyDrag){
33806             this.proxy.show();
33807         }
33808         if(this.constraintoviewport !== false){
33809             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33810         }
33811     },
33812
33813     // private
33814     endMove : function(){
33815         if(!this.proxyDrag){
33816             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33817         }else{
33818             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33819             this.proxy.hide();
33820         }
33821         this.refreshSize();
33822         this.adjustAssets();
33823         this.focus();
33824         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33825     },
33826
33827     /**
33828      * Brings this dialog to the front of any other visible dialogs
33829      * @return {Roo.BasicDialog} this
33830      */
33831     toFront : function(){
33832         Roo.DialogManager.bringToFront(this);
33833         return this;
33834     },
33835
33836     /**
33837      * Sends this dialog to the back (under) of any other visible dialogs
33838      * @return {Roo.BasicDialog} this
33839      */
33840     toBack : function(){
33841         Roo.DialogManager.sendToBack(this);
33842         return this;
33843     },
33844
33845     /**
33846      * Centers this dialog in the viewport
33847      * @return {Roo.BasicDialog} this
33848      */
33849     center : function(){
33850         var xy = this.el.getCenterXY(true);
33851         this.moveTo(xy[0], xy[1]);
33852         return this;
33853     },
33854
33855     /**
33856      * Moves the dialog's top-left corner to the specified point
33857      * @param {Number} x
33858      * @param {Number} y
33859      * @return {Roo.BasicDialog} this
33860      */
33861     moveTo : function(x, y){
33862         this.xy = [x,y];
33863         if(this.isVisible()){
33864             this.el.setXY(this.xy);
33865             this.adjustAssets();
33866         }
33867         return this;
33868     },
33869
33870     /**
33871      * Aligns the dialog to the specified element
33872      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33873      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33874      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33875      * @return {Roo.BasicDialog} this
33876      */
33877     alignTo : function(element, position, offsets){
33878         this.xy = this.el.getAlignToXY(element, position, offsets);
33879         if(this.isVisible()){
33880             this.el.setXY(this.xy);
33881             this.adjustAssets();
33882         }
33883         return this;
33884     },
33885
33886     /**
33887      * Anchors an element to another element and realigns it when the window is resized.
33888      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33889      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33890      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33891      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33892      * is a number, it is used as the buffer delay (defaults to 50ms).
33893      * @return {Roo.BasicDialog} this
33894      */
33895     anchorTo : function(el, alignment, offsets, monitorScroll){
33896         var action = function(){
33897             this.alignTo(el, alignment, offsets);
33898         };
33899         Roo.EventManager.onWindowResize(action, this);
33900         var tm = typeof monitorScroll;
33901         if(tm != 'undefined'){
33902             Roo.EventManager.on(window, 'scroll', action, this,
33903                 {buffer: tm == 'number' ? monitorScroll : 50});
33904         }
33905         action.call(this);
33906         return this;
33907     },
33908
33909     /**
33910      * Returns true if the dialog is visible
33911      * @return {Boolean}
33912      */
33913     isVisible : function(){
33914         return this.el.isVisible();
33915     },
33916
33917     // private
33918     animHide : function(callback){
33919         var b = Roo.get(this.animateTarget).getBox();
33920         this.proxy.show();
33921         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33922         this.el.hide();
33923         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33924                     this.hideEl.createDelegate(this, [callback]));
33925     },
33926
33927     /**
33928      * Hides the dialog.
33929      * @param {Function} callback (optional) Function to call when the dialog is hidden
33930      * @return {Roo.BasicDialog} this
33931      */
33932     hide : function(callback){
33933         if (this.fireEvent("beforehide", this) === false){
33934             return;
33935         }
33936         if(this.shadow){
33937             this.shadow.hide();
33938         }
33939         if(this.shim) {
33940           this.shim.hide();
33941         }
33942         // sometimes animateTarget seems to get set.. causing problems...
33943         // this just double checks..
33944         if(this.animateTarget && Roo.get(this.animateTarget)) {
33945            this.animHide(callback);
33946         }else{
33947             this.el.hide();
33948             this.hideEl(callback);
33949         }
33950         return this;
33951     },
33952
33953     // private
33954     hideEl : function(callback){
33955         this.proxy.hide();
33956         if(this.modal){
33957             this.mask.hide();
33958             Roo.get(document.body).removeClass("x-body-masked");
33959         }
33960         this.fireEvent("hide", this);
33961         if(typeof callback == "function"){
33962             callback();
33963         }
33964     },
33965
33966     // private
33967     hideAction : function(){
33968         this.setLeft("-10000px");
33969         this.setTop("-10000px");
33970         this.setStyle("visibility", "hidden");
33971     },
33972
33973     // private
33974     refreshSize : function(){
33975         this.size = this.el.getSize();
33976         this.xy = this.el.getXY();
33977         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33978     },
33979
33980     // private
33981     // z-index is managed by the DialogManager and may be overwritten at any time
33982     setZIndex : function(index){
33983         if(this.modal){
33984             this.mask.setStyle("z-index", index);
33985         }
33986         if(this.shim){
33987             this.shim.setStyle("z-index", ++index);
33988         }
33989         if(this.shadow){
33990             this.shadow.setZIndex(++index);
33991         }
33992         this.el.setStyle("z-index", ++index);
33993         if(this.proxy){
33994             this.proxy.setStyle("z-index", ++index);
33995         }
33996         if(this.resizer){
33997             this.resizer.proxy.setStyle("z-index", ++index);
33998         }
33999
34000         this.lastZIndex = index;
34001     },
34002
34003     /**
34004      * Returns the element for this dialog
34005      * @return {Roo.Element} The underlying dialog Element
34006      */
34007     getEl : function(){
34008         return this.el;
34009     }
34010 });
34011
34012 /**
34013  * @class Roo.DialogManager
34014  * Provides global access to BasicDialogs that have been created and
34015  * support for z-indexing (layering) multiple open dialogs.
34016  */
34017 Roo.DialogManager = function(){
34018     var list = {};
34019     var accessList = [];
34020     var front = null;
34021
34022     // private
34023     var sortDialogs = function(d1, d2){
34024         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34025     };
34026
34027     // private
34028     var orderDialogs = function(){
34029         accessList.sort(sortDialogs);
34030         var seed = Roo.DialogManager.zseed;
34031         for(var i = 0, len = accessList.length; i < len; i++){
34032             var dlg = accessList[i];
34033             if(dlg){
34034                 dlg.setZIndex(seed + (i*10));
34035             }
34036         }
34037     };
34038
34039     return {
34040         /**
34041          * The starting z-index for BasicDialogs (defaults to 9000)
34042          * @type Number The z-index value
34043          */
34044         zseed : 9000,
34045
34046         // private
34047         register : function(dlg){
34048             list[dlg.id] = dlg;
34049             accessList.push(dlg);
34050         },
34051
34052         // private
34053         unregister : function(dlg){
34054             delete list[dlg.id];
34055             var i=0;
34056             var len=0;
34057             if(!accessList.indexOf){
34058                 for(  i = 0, len = accessList.length; i < len; i++){
34059                     if(accessList[i] == dlg){
34060                         accessList.splice(i, 1);
34061                         return;
34062                     }
34063                 }
34064             }else{
34065                  i = accessList.indexOf(dlg);
34066                 if(i != -1){
34067                     accessList.splice(i, 1);
34068                 }
34069             }
34070         },
34071
34072         /**
34073          * Gets a registered dialog by id
34074          * @param {String/Object} id The id of the dialog or a dialog
34075          * @return {Roo.BasicDialog} this
34076          */
34077         get : function(id){
34078             return typeof id == "object" ? id : list[id];
34079         },
34080
34081         /**
34082          * Brings the specified dialog to the front
34083          * @param {String/Object} dlg The id of the dialog or a dialog
34084          * @return {Roo.BasicDialog} this
34085          */
34086         bringToFront : function(dlg){
34087             dlg = this.get(dlg);
34088             if(dlg != front){
34089                 front = dlg;
34090                 dlg._lastAccess = new Date().getTime();
34091                 orderDialogs();
34092             }
34093             return dlg;
34094         },
34095
34096         /**
34097          * Sends the specified dialog to the back
34098          * @param {String/Object} dlg The id of the dialog or a dialog
34099          * @return {Roo.BasicDialog} this
34100          */
34101         sendToBack : function(dlg){
34102             dlg = this.get(dlg);
34103             dlg._lastAccess = -(new Date().getTime());
34104             orderDialogs();
34105             return dlg;
34106         },
34107
34108         /**
34109          * Hides all dialogs
34110          */
34111         hideAll : function(){
34112             for(var id in list){
34113                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34114                     list[id].hide();
34115                 }
34116             }
34117         }
34118     };
34119 }();
34120
34121 /**
34122  * @class Roo.LayoutDialog
34123  * @extends Roo.BasicDialog
34124  * @children Roo.ContentPanel
34125  * @parent builder none
34126  * Dialog which provides adjustments for working with a layout in a Dialog.
34127  * Add your necessary layout config options to the dialog's config.<br>
34128  * Example usage (including a nested layout):
34129  * <pre><code>
34130 if(!dialog){
34131     dialog = new Roo.LayoutDialog("download-dlg", {
34132         modal: true,
34133         width:600,
34134         height:450,
34135         shadow:true,
34136         minWidth:500,
34137         minHeight:350,
34138         autoTabs:true,
34139         proxyDrag:true,
34140         // layout config merges with the dialog config
34141         center:{
34142             tabPosition: "top",
34143             alwaysShowTabs: true
34144         }
34145     });
34146     dialog.addKeyListener(27, dialog.hide, dialog);
34147     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34148     dialog.addButton("Build It!", this.getDownload, this);
34149
34150     // we can even add nested layouts
34151     var innerLayout = new Roo.BorderLayout("dl-inner", {
34152         east: {
34153             initialSize: 200,
34154             autoScroll:true,
34155             split:true
34156         },
34157         center: {
34158             autoScroll:true
34159         }
34160     });
34161     innerLayout.beginUpdate();
34162     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34163     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34164     innerLayout.endUpdate(true);
34165
34166     var layout = dialog.getLayout();
34167     layout.beginUpdate();
34168     layout.add("center", new Roo.ContentPanel("standard-panel",
34169                         {title: "Download the Source", fitToFrame:true}));
34170     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34171                {title: "Build your own roo.js"}));
34172     layout.getRegion("center").showPanel(sp);
34173     layout.endUpdate();
34174 }
34175 </code></pre>
34176     * @constructor
34177     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34178     * @param {Object} config configuration options
34179   */
34180 Roo.LayoutDialog = function(el, cfg){
34181     
34182     var config=  cfg;
34183     if (typeof(cfg) == 'undefined') {
34184         config = Roo.apply({}, el);
34185         // not sure why we use documentElement here.. - it should always be body.
34186         // IE7 borks horribly if we use documentElement.
34187         // webkit also does not like documentElement - it creates a body element...
34188         el = Roo.get( document.body || document.documentElement ).createChild();
34189         //config.autoCreate = true;
34190     }
34191     
34192     
34193     config.autoTabs = false;
34194     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34195     this.body.setStyle({overflow:"hidden", position:"relative"});
34196     this.layout = new Roo.BorderLayout(this.body.dom, config);
34197     this.layout.monitorWindowResize = false;
34198     this.el.addClass("x-dlg-auto-layout");
34199     // fix case when center region overwrites center function
34200     this.center = Roo.BasicDialog.prototype.center;
34201     this.on("show", this.layout.layout, this.layout, true);
34202     if (config.items) {
34203         var xitems = config.items;
34204         delete config.items;
34205         Roo.each(xitems, this.addxtype, this);
34206     }
34207     
34208     
34209 };
34210 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34211     
34212     
34213     /**
34214      * @cfg {Roo.LayoutRegion} east  
34215      */
34216     /**
34217      * @cfg {Roo.LayoutRegion} west
34218      */
34219     /**
34220      * @cfg {Roo.LayoutRegion} south
34221      */
34222     /**
34223      * @cfg {Roo.LayoutRegion} north
34224      */
34225     /**
34226      * @cfg {Roo.LayoutRegion} center
34227      */
34228     /**
34229      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34230      */
34231     
34232     
34233     /**
34234      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34235      * @deprecated
34236      */
34237     endUpdate : function(){
34238         this.layout.endUpdate();
34239     },
34240
34241     /**
34242      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34243      *  @deprecated
34244      */
34245     beginUpdate : function(){
34246         this.layout.beginUpdate();
34247     },
34248
34249     /**
34250      * Get the BorderLayout for this dialog
34251      * @return {Roo.BorderLayout}
34252      */
34253     getLayout : function(){
34254         return this.layout;
34255     },
34256
34257     showEl : function(){
34258         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34259         if(Roo.isIE7){
34260             this.layout.layout();
34261         }
34262     },
34263
34264     // private
34265     // Use the syncHeightBeforeShow config option to control this automatically
34266     syncBodyHeight : function(){
34267         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34268         if(this.layout){this.layout.layout();}
34269     },
34270     
34271       /**
34272      * Add an xtype element (actually adds to the layout.)
34273      * @return {Object} xdata xtype object data.
34274      */
34275     
34276     addxtype : function(c) {
34277         return this.layout.addxtype(c);
34278     }
34279 });/*
34280  * Based on:
34281  * Ext JS Library 1.1.1
34282  * Copyright(c) 2006-2007, Ext JS, LLC.
34283  *
34284  * Originally Released Under LGPL - original licence link has changed is not relivant.
34285  *
34286  * Fork - LGPL
34287  * <script type="text/javascript">
34288  */
34289  
34290 /**
34291  * @class Roo.MessageBox
34292  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34293  * Example usage:
34294  *<pre><code>
34295 // Basic alert:
34296 Roo.Msg.alert('Status', 'Changes saved successfully.');
34297
34298 // Prompt for user data:
34299 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34300     if (btn == 'ok'){
34301         // process text value...
34302     }
34303 });
34304
34305 // Show a dialog using config options:
34306 Roo.Msg.show({
34307    title:'Save Changes?',
34308    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34309    buttons: Roo.Msg.YESNOCANCEL,
34310    fn: processResult,
34311    animEl: 'elId'
34312 });
34313 </code></pre>
34314  * @static
34315  */
34316 Roo.MessageBox = function(){
34317     var dlg, opt, mask, waitTimer;
34318     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34319     var buttons, activeTextEl, bwidth;
34320
34321     // private
34322     var handleButton = function(button){
34323         dlg.hide();
34324         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34325     };
34326
34327     // private
34328     var handleHide = function(){
34329         if(opt && opt.cls){
34330             dlg.el.removeClass(opt.cls);
34331         }
34332         if(waitTimer){
34333             Roo.TaskMgr.stop(waitTimer);
34334             waitTimer = null;
34335         }
34336     };
34337
34338     // private
34339     var updateButtons = function(b){
34340         var width = 0;
34341         if(!b){
34342             buttons["ok"].hide();
34343             buttons["cancel"].hide();
34344             buttons["yes"].hide();
34345             buttons["no"].hide();
34346             dlg.footer.dom.style.display = 'none';
34347             return width;
34348         }
34349         dlg.footer.dom.style.display = '';
34350         for(var k in buttons){
34351             if(typeof buttons[k] != "function"){
34352                 if(b[k]){
34353                     buttons[k].show();
34354                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34355                     width += buttons[k].el.getWidth()+15;
34356                 }else{
34357                     buttons[k].hide();
34358                 }
34359             }
34360         }
34361         return width;
34362     };
34363
34364     // private
34365     var handleEsc = function(d, k, e){
34366         if(opt && opt.closable !== false){
34367             dlg.hide();
34368         }
34369         if(e){
34370             e.stopEvent();
34371         }
34372     };
34373
34374     return {
34375         /**
34376          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34377          * @return {Roo.BasicDialog} The BasicDialog element
34378          */
34379         getDialog : function(){
34380            if(!dlg){
34381                 dlg = new Roo.BasicDialog("x-msg-box", {
34382                     autoCreate : true,
34383                     shadow: true,
34384                     draggable: true,
34385                     resizable:false,
34386                     constraintoviewport:false,
34387                     fixedcenter:true,
34388                     collapsible : false,
34389                     shim:true,
34390                     modal: true,
34391                     width:400, height:100,
34392                     buttonAlign:"center",
34393                     closeClick : function(){
34394                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34395                             handleButton("no");
34396                         }else{
34397                             handleButton("cancel");
34398                         }
34399                     }
34400                 });
34401                 dlg.on("hide", handleHide);
34402                 mask = dlg.mask;
34403                 dlg.addKeyListener(27, handleEsc);
34404                 buttons = {};
34405                 var bt = this.buttonText;
34406                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34407                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34408                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34409                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34410                 bodyEl = dlg.body.createChild({
34411
34412                     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>'
34413                 });
34414                 msgEl = bodyEl.dom.firstChild;
34415                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34416                 textboxEl.enableDisplayMode();
34417                 textboxEl.addKeyListener([10,13], function(){
34418                     if(dlg.isVisible() && opt && opt.buttons){
34419                         if(opt.buttons.ok){
34420                             handleButton("ok");
34421                         }else if(opt.buttons.yes){
34422                             handleButton("yes");
34423                         }
34424                     }
34425                 });
34426                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34427                 textareaEl.enableDisplayMode();
34428                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34429                 progressEl.enableDisplayMode();
34430                 var pf = progressEl.dom.firstChild;
34431                 if (pf) {
34432                     pp = Roo.get(pf.firstChild);
34433                     pp.setHeight(pf.offsetHeight);
34434                 }
34435                 
34436             }
34437             return dlg;
34438         },
34439
34440         /**
34441          * Updates the message box body text
34442          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34443          * the XHTML-compliant non-breaking space character '&amp;#160;')
34444          * @return {Roo.MessageBox} This message box
34445          */
34446         updateText : function(text){
34447             if(!dlg.isVisible() && !opt.width){
34448                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34449             }
34450             msgEl.innerHTML = text || '&#160;';
34451       
34452             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34453             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34454             var w = Math.max(
34455                     Math.min(opt.width || cw , this.maxWidth), 
34456                     Math.max(opt.minWidth || this.minWidth, bwidth)
34457             );
34458             if(opt.prompt){
34459                 activeTextEl.setWidth(w);
34460             }
34461             if(dlg.isVisible()){
34462                 dlg.fixedcenter = false;
34463             }
34464             // to big, make it scroll. = But as usual stupid IE does not support
34465             // !important..
34466             
34467             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34468                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34469                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34470             } else {
34471                 bodyEl.dom.style.height = '';
34472                 bodyEl.dom.style.overflowY = '';
34473             }
34474             if (cw > w) {
34475                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34476             } else {
34477                 bodyEl.dom.style.overflowX = '';
34478             }
34479             
34480             dlg.setContentSize(w, bodyEl.getHeight());
34481             if(dlg.isVisible()){
34482                 dlg.fixedcenter = true;
34483             }
34484             return this;
34485         },
34486
34487         /**
34488          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34489          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34490          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34491          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34492          * @return {Roo.MessageBox} This message box
34493          */
34494         updateProgress : function(value, text){
34495             if(text){
34496                 this.updateText(text);
34497             }
34498             if (pp) { // weird bug on my firefox - for some reason this is not defined
34499                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34500             }
34501             return this;
34502         },        
34503
34504         /**
34505          * Returns true if the message box is currently displayed
34506          * @return {Boolean} True if the message box is visible, else false
34507          */
34508         isVisible : function(){
34509             return dlg && dlg.isVisible();  
34510         },
34511
34512         /**
34513          * Hides the message box if it is displayed
34514          */
34515         hide : function(){
34516             if(this.isVisible()){
34517                 dlg.hide();
34518             }  
34519         },
34520
34521         /**
34522          * Displays a new message box, or reinitializes an existing message box, based on the config options
34523          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34524          * The following config object properties are supported:
34525          * <pre>
34526 Property    Type             Description
34527 ----------  ---------------  ------------------------------------------------------------------------------------
34528 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34529                                    closes (defaults to undefined)
34530 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34531                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34532 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34533                                    progress and wait dialogs will ignore this property and always hide the
34534                                    close button as they can only be closed programmatically.
34535 cls               String           A custom CSS class to apply to the message box element
34536 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34537                                    displayed (defaults to 75)
34538 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34539                                    function will be btn (the name of the button that was clicked, if applicable,
34540                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34541                                    Progress and wait dialogs will ignore this option since they do not respond to
34542                                    user actions and can only be closed programmatically, so any required function
34543                                    should be called by the same code after it closes the dialog.
34544 icon              String           A CSS class that provides a background image to be used as an icon for
34545                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34546 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34547 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34548 modal             Boolean          False to allow user interaction with the page while the message box is
34549                                    displayed (defaults to true)
34550 msg               String           A string that will replace the existing message box body text (defaults
34551                                    to the XHTML-compliant non-breaking space character '&#160;')
34552 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34553 progress          Boolean          True to display a progress bar (defaults to false)
34554 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34555 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34556 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34557 title             String           The title text
34558 value             String           The string value to set into the active textbox element if displayed
34559 wait              Boolean          True to display a progress bar (defaults to false)
34560 width             Number           The width of the dialog in pixels
34561 </pre>
34562          *
34563          * Example usage:
34564          * <pre><code>
34565 Roo.Msg.show({
34566    title: 'Address',
34567    msg: 'Please enter your address:',
34568    width: 300,
34569    buttons: Roo.MessageBox.OKCANCEL,
34570    multiline: true,
34571    fn: saveAddress,
34572    animEl: 'addAddressBtn'
34573 });
34574 </code></pre>
34575          * @param {Object} config Configuration options
34576          * @return {Roo.MessageBox} This message box
34577          */
34578         show : function(options)
34579         {
34580             
34581             // this causes nightmares if you show one dialog after another
34582             // especially on callbacks..
34583              
34584             if(this.isVisible()){
34585                 
34586                 this.hide();
34587                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34588                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34589                 Roo.log("New Dialog Message:" +  options.msg )
34590                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34591                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34592                 
34593             }
34594             var d = this.getDialog();
34595             opt = options;
34596             d.setTitle(opt.title || "&#160;");
34597             d.close.setDisplayed(opt.closable !== false);
34598             activeTextEl = textboxEl;
34599             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34600             if(opt.prompt){
34601                 if(opt.multiline){
34602                     textboxEl.hide();
34603                     textareaEl.show();
34604                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34605                         opt.multiline : this.defaultTextHeight);
34606                     activeTextEl = textareaEl;
34607                 }else{
34608                     textboxEl.show();
34609                     textareaEl.hide();
34610                 }
34611             }else{
34612                 textboxEl.hide();
34613                 textareaEl.hide();
34614             }
34615             progressEl.setDisplayed(opt.progress === true);
34616             this.updateProgress(0);
34617             activeTextEl.dom.value = opt.value || "";
34618             if(opt.prompt){
34619                 dlg.setDefaultButton(activeTextEl);
34620             }else{
34621                 var bs = opt.buttons;
34622                 var db = null;
34623                 if(bs && bs.ok){
34624                     db = buttons["ok"];
34625                 }else if(bs && bs.yes){
34626                     db = buttons["yes"];
34627                 }
34628                 dlg.setDefaultButton(db);
34629             }
34630             bwidth = updateButtons(opt.buttons);
34631             this.updateText(opt.msg);
34632             if(opt.cls){
34633                 d.el.addClass(opt.cls);
34634             }
34635             d.proxyDrag = opt.proxyDrag === true;
34636             d.modal = opt.modal !== false;
34637             d.mask = opt.modal !== false ? mask : false;
34638             if(!d.isVisible()){
34639                 // force it to the end of the z-index stack so it gets a cursor in FF
34640                 document.body.appendChild(dlg.el.dom);
34641                 d.animateTarget = null;
34642                 d.show(options.animEl);
34643             }
34644             return this;
34645         },
34646
34647         /**
34648          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34649          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34650          * and closing the message box when the process is complete.
34651          * @param {String} title The title bar text
34652          * @param {String} msg The message box body text
34653          * @return {Roo.MessageBox} This message box
34654          */
34655         progress : function(title, msg){
34656             this.show({
34657                 title : title,
34658                 msg : msg,
34659                 buttons: false,
34660                 progress:true,
34661                 closable:false,
34662                 minWidth: this.minProgressWidth,
34663                 modal : true
34664             });
34665             return this;
34666         },
34667
34668         /**
34669          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34670          * If a callback function is passed it will be called after the user clicks the button, and the
34671          * id of the button that was clicked will be passed as the only parameter to the callback
34672          * (could also be the top-right close button).
34673          * @param {String} title The title bar text
34674          * @param {String} msg The message box body text
34675          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34676          * @param {Object} scope (optional) The scope of the callback function
34677          * @return {Roo.MessageBox} This message box
34678          */
34679         alert : function(title, msg, fn, scope){
34680             this.show({
34681                 title : title,
34682                 msg : msg,
34683                 buttons: this.OK,
34684                 fn: fn,
34685                 scope : scope,
34686                 modal : true
34687             });
34688             return this;
34689         },
34690
34691         /**
34692          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34693          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34694          * You are responsible for closing the message box when the process is complete.
34695          * @param {String} msg The message box body text
34696          * @param {String} title (optional) The title bar text
34697          * @return {Roo.MessageBox} This message box
34698          */
34699         wait : function(msg, title){
34700             this.show({
34701                 title : title,
34702                 msg : msg,
34703                 buttons: false,
34704                 closable:false,
34705                 progress:true,
34706                 modal:true,
34707                 width:300,
34708                 wait:true
34709             });
34710             waitTimer = Roo.TaskMgr.start({
34711                 run: function(i){
34712                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34713                 },
34714                 interval: 1000
34715             });
34716             return this;
34717         },
34718
34719         /**
34720          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34721          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34722          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34723          * @param {String} title The title bar text
34724          * @param {String} msg The message box body text
34725          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34726          * @param {Object} scope (optional) The scope of the callback function
34727          * @return {Roo.MessageBox} This message box
34728          */
34729         confirm : function(title, msg, fn, scope){
34730             this.show({
34731                 title : title,
34732                 msg : msg,
34733                 buttons: this.YESNO,
34734                 fn: fn,
34735                 scope : scope,
34736                 modal : true
34737             });
34738             return this;
34739         },
34740
34741         /**
34742          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34743          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34744          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34745          * (could also be the top-right close button) and the text that was entered will be passed as the two
34746          * parameters to the callback.
34747          * @param {String} title The title bar text
34748          * @param {String} msg The message box body text
34749          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34750          * @param {Object} scope (optional) The scope of the callback function
34751          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34752          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34753          * @return {Roo.MessageBox} This message box
34754          */
34755         prompt : function(title, msg, fn, scope, multiline){
34756             this.show({
34757                 title : title,
34758                 msg : msg,
34759                 buttons: this.OKCANCEL,
34760                 fn: fn,
34761                 minWidth:250,
34762                 scope : scope,
34763                 prompt:true,
34764                 multiline: multiline,
34765                 modal : true
34766             });
34767             return this;
34768         },
34769
34770         /**
34771          * Button config that displays a single OK button
34772          * @type Object
34773          */
34774         OK : {ok:true},
34775         /**
34776          * Button config that displays Yes and No buttons
34777          * @type Object
34778          */
34779         YESNO : {yes:true, no:true},
34780         /**
34781          * Button config that displays OK and Cancel buttons
34782          * @type Object
34783          */
34784         OKCANCEL : {ok:true, cancel:true},
34785         /**
34786          * Button config that displays Yes, No and Cancel buttons
34787          * @type Object
34788          */
34789         YESNOCANCEL : {yes:true, no:true, cancel:true},
34790
34791         /**
34792          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34793          * @type Number
34794          */
34795         defaultTextHeight : 75,
34796         /**
34797          * The maximum width in pixels of the message box (defaults to 600)
34798          * @type Number
34799          */
34800         maxWidth : 600,
34801         /**
34802          * The minimum width in pixels of the message box (defaults to 100)
34803          * @type Number
34804          */
34805         minWidth : 100,
34806         /**
34807          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34808          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34809          * @type Number
34810          */
34811         minProgressWidth : 250,
34812         /**
34813          * An object containing the default button text strings that can be overriden for localized language support.
34814          * Supported properties are: ok, cancel, yes and no.
34815          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34816          * @type Object
34817          */
34818         buttonText : {
34819             ok : "OK",
34820             cancel : "Cancel",
34821             yes : "Yes",
34822             no : "No"
34823         }
34824     };
34825 }();
34826
34827 /**
34828  * Shorthand for {@link Roo.MessageBox}
34829  */
34830 Roo.Msg = Roo.MessageBox;/*
34831  * Based on:
34832  * Ext JS Library 1.1.1
34833  * Copyright(c) 2006-2007, Ext JS, LLC.
34834  *
34835  * Originally Released Under LGPL - original licence link has changed is not relivant.
34836  *
34837  * Fork - LGPL
34838  * <script type="text/javascript">
34839  */
34840 /**
34841  * @class Roo.QuickTips
34842  * Provides attractive and customizable tooltips for any element.
34843  * @static
34844  */
34845 Roo.QuickTips = function(){
34846     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34847     var ce, bd, xy, dd;
34848     var visible = false, disabled = true, inited = false;
34849     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34850     
34851     var onOver = function(e){
34852         if(disabled){
34853             return;
34854         }
34855         var t = e.getTarget();
34856         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34857             return;
34858         }
34859         if(ce && t == ce.el){
34860             clearTimeout(hideProc);
34861             return;
34862         }
34863         if(t && tagEls[t.id]){
34864             tagEls[t.id].el = t;
34865             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34866             return;
34867         }
34868         var ttp, et = Roo.fly(t);
34869         var ns = cfg.namespace;
34870         if(tm.interceptTitles && t.title){
34871             ttp = t.title;
34872             t.qtip = ttp;
34873             t.removeAttribute("title");
34874             e.preventDefault();
34875         }else{
34876             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34877         }
34878         if(ttp){
34879             showProc = show.defer(tm.showDelay, tm, [{
34880                 el: t, 
34881                 text: ttp.replace(/\\n/g,'<br/>'),
34882                 width: et.getAttributeNS(ns, cfg.width),
34883                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34884                 title: et.getAttributeNS(ns, cfg.title),
34885                     cls: et.getAttributeNS(ns, cfg.cls)
34886             }]);
34887         }
34888     };
34889     
34890     var onOut = function(e){
34891         clearTimeout(showProc);
34892         var t = e.getTarget();
34893         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34894             hideProc = setTimeout(hide, tm.hideDelay);
34895         }
34896     };
34897     
34898     var onMove = function(e){
34899         if(disabled){
34900             return;
34901         }
34902         xy = e.getXY();
34903         xy[1] += 18;
34904         if(tm.trackMouse && ce){
34905             el.setXY(xy);
34906         }
34907     };
34908     
34909     var onDown = function(e){
34910         clearTimeout(showProc);
34911         clearTimeout(hideProc);
34912         if(!e.within(el)){
34913             if(tm.hideOnClick){
34914                 hide();
34915                 tm.disable();
34916                 tm.enable.defer(100, tm);
34917             }
34918         }
34919     };
34920     
34921     var getPad = function(){
34922         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34923     };
34924
34925     var show = function(o){
34926         if(disabled){
34927             return;
34928         }
34929         clearTimeout(dismissProc);
34930         ce = o;
34931         if(removeCls){ // in case manually hidden
34932             el.removeClass(removeCls);
34933             removeCls = null;
34934         }
34935         if(ce.cls){
34936             el.addClass(ce.cls);
34937             removeCls = ce.cls;
34938         }
34939         if(ce.title){
34940             tipTitle.update(ce.title);
34941             tipTitle.show();
34942         }else{
34943             tipTitle.update('');
34944             tipTitle.hide();
34945         }
34946         el.dom.style.width  = tm.maxWidth+'px';
34947         //tipBody.dom.style.width = '';
34948         tipBodyText.update(o.text);
34949         var p = getPad(), w = ce.width;
34950         if(!w){
34951             var td = tipBodyText.dom;
34952             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34953             if(aw > tm.maxWidth){
34954                 w = tm.maxWidth;
34955             }else if(aw < tm.minWidth){
34956                 w = tm.minWidth;
34957             }else{
34958                 w = aw;
34959             }
34960         }
34961         //tipBody.setWidth(w);
34962         el.setWidth(parseInt(w, 10) + p);
34963         if(ce.autoHide === false){
34964             close.setDisplayed(true);
34965             if(dd){
34966                 dd.unlock();
34967             }
34968         }else{
34969             close.setDisplayed(false);
34970             if(dd){
34971                 dd.lock();
34972             }
34973         }
34974         if(xy){
34975             el.avoidY = xy[1]-18;
34976             el.setXY(xy);
34977         }
34978         if(tm.animate){
34979             el.setOpacity(.1);
34980             el.setStyle("visibility", "visible");
34981             el.fadeIn({callback: afterShow});
34982         }else{
34983             afterShow();
34984         }
34985     };
34986     
34987     var afterShow = function(){
34988         if(ce){
34989             el.show();
34990             esc.enable();
34991             if(tm.autoDismiss && ce.autoHide !== false){
34992                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34993             }
34994         }
34995     };
34996     
34997     var hide = function(noanim){
34998         clearTimeout(dismissProc);
34999         clearTimeout(hideProc);
35000         ce = null;
35001         if(el.isVisible()){
35002             esc.disable();
35003             if(noanim !== true && tm.animate){
35004                 el.fadeOut({callback: afterHide});
35005             }else{
35006                 afterHide();
35007             } 
35008         }
35009     };
35010     
35011     var afterHide = function(){
35012         el.hide();
35013         if(removeCls){
35014             el.removeClass(removeCls);
35015             removeCls = null;
35016         }
35017     };
35018     
35019     return {
35020         /**
35021         * @cfg {Number} minWidth
35022         * The minimum width of the quick tip (defaults to 40)
35023         */
35024        minWidth : 40,
35025         /**
35026         * @cfg {Number} maxWidth
35027         * The maximum width of the quick tip (defaults to 300)
35028         */
35029        maxWidth : 300,
35030         /**
35031         * @cfg {Boolean} interceptTitles
35032         * True to automatically use the element's DOM title value if available (defaults to false)
35033         */
35034        interceptTitles : false,
35035         /**
35036         * @cfg {Boolean} trackMouse
35037         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35038         */
35039        trackMouse : false,
35040         /**
35041         * @cfg {Boolean} hideOnClick
35042         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35043         */
35044        hideOnClick : true,
35045         /**
35046         * @cfg {Number} showDelay
35047         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35048         */
35049        showDelay : 500,
35050         /**
35051         * @cfg {Number} hideDelay
35052         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35053         */
35054        hideDelay : 200,
35055         /**
35056         * @cfg {Boolean} autoHide
35057         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35058         * Used in conjunction with hideDelay.
35059         */
35060        autoHide : true,
35061         /**
35062         * @cfg {Boolean}
35063         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35064         * (defaults to true).  Used in conjunction with autoDismissDelay.
35065         */
35066        autoDismiss : true,
35067         /**
35068         * @cfg {Number}
35069         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35070         */
35071        autoDismissDelay : 5000,
35072        /**
35073         * @cfg {Boolean} animate
35074         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35075         */
35076        animate : false,
35077
35078        /**
35079         * @cfg {String} title
35080         * Title text to display (defaults to '').  This can be any valid HTML markup.
35081         */
35082         title: '',
35083        /**
35084         * @cfg {String} text
35085         * Body text to display (defaults to '').  This can be any valid HTML markup.
35086         */
35087         text : '',
35088        /**
35089         * @cfg {String} cls
35090         * A CSS class to apply to the base quick tip element (defaults to '').
35091         */
35092         cls : '',
35093        /**
35094         * @cfg {Number} width
35095         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35096         * minWidth or maxWidth.
35097         */
35098         width : null,
35099
35100     /**
35101      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35102      * or display QuickTips in a page.
35103      */
35104        init : function(){
35105           tm = Roo.QuickTips;
35106           cfg = tm.tagConfig;
35107           if(!inited){
35108               if(!Roo.isReady){ // allow calling of init() before onReady
35109                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35110                   return;
35111               }
35112               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35113               el.fxDefaults = {stopFx: true};
35114               // maximum custom styling
35115               //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>');
35116               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>');              
35117               tipTitle = el.child('h3');
35118               tipTitle.enableDisplayMode("block");
35119               tipBody = el.child('div.x-tip-bd');
35120               tipBodyText = el.child('div.x-tip-bd-inner');
35121               //bdLeft = el.child('div.x-tip-bd-left');
35122               //bdRight = el.child('div.x-tip-bd-right');
35123               close = el.child('div.x-tip-close');
35124               close.enableDisplayMode("block");
35125               close.on("click", hide);
35126               var d = Roo.get(document);
35127               d.on("mousedown", onDown);
35128               d.on("mouseover", onOver);
35129               d.on("mouseout", onOut);
35130               d.on("mousemove", onMove);
35131               esc = d.addKeyListener(27, hide);
35132               esc.disable();
35133               if(Roo.dd.DD){
35134                   dd = el.initDD("default", null, {
35135                       onDrag : function(){
35136                           el.sync();  
35137                       }
35138                   });
35139                   dd.setHandleElId(tipTitle.id);
35140                   dd.lock();
35141               }
35142               inited = true;
35143           }
35144           this.enable(); 
35145        },
35146
35147     /**
35148      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35149      * are supported:
35150      * <pre>
35151 Property    Type                   Description
35152 ----------  ---------------------  ------------------------------------------------------------------------
35153 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35154      * </ul>
35155      * @param {Object} config The config object
35156      */
35157        register : function(config){
35158            var cs = config instanceof Array ? config : arguments;
35159            for(var i = 0, len = cs.length; i < len; i++) {
35160                var c = cs[i];
35161                var target = c.target;
35162                if(target){
35163                    if(target instanceof Array){
35164                        for(var j = 0, jlen = target.length; j < jlen; j++){
35165                            tagEls[target[j]] = c;
35166                        }
35167                    }else{
35168                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35169                    }
35170                }
35171            }
35172        },
35173
35174     /**
35175      * Removes this quick tip from its element and destroys it.
35176      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35177      */
35178        unregister : function(el){
35179            delete tagEls[Roo.id(el)];
35180        },
35181
35182     /**
35183      * Enable this quick tip.
35184      */
35185        enable : function(){
35186            if(inited && disabled){
35187                locks.pop();
35188                if(locks.length < 1){
35189                    disabled = false;
35190                }
35191            }
35192        },
35193
35194     /**
35195      * Disable this quick tip.
35196      */
35197        disable : function(){
35198           disabled = true;
35199           clearTimeout(showProc);
35200           clearTimeout(hideProc);
35201           clearTimeout(dismissProc);
35202           if(ce){
35203               hide(true);
35204           }
35205           locks.push(1);
35206        },
35207
35208     /**
35209      * Returns true if the quick tip is enabled, else false.
35210      */
35211        isEnabled : function(){
35212             return !disabled;
35213        },
35214
35215         // private
35216        tagConfig : {
35217            namespace : "roo", // was ext?? this may break..
35218            alt_namespace : "ext",
35219            attribute : "qtip",
35220            width : "width",
35221            target : "target",
35222            title : "qtitle",
35223            hide : "hide",
35224            cls : "qclass"
35225        }
35226    };
35227 }();
35228
35229 // backwards compat
35230 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241
35242 /**
35243  * @class Roo.tree.TreePanel
35244  * @extends Roo.data.Tree
35245  * @cfg {Roo.tree.TreeNode} root The root node
35246  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35247  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35248  * @cfg {Boolean} enableDD true to enable drag and drop
35249  * @cfg {Boolean} enableDrag true to enable just drag
35250  * @cfg {Boolean} enableDrop true to enable just drop
35251  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35252  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35253  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35254  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35255  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35256  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35257  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35258  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35259  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35260  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35261  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35262  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35263  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35264  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35265  * @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>
35266  * @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>
35267  * 
35268  * @constructor
35269  * @param {String/HTMLElement/Element} el The container element
35270  * @param {Object} config
35271  */
35272 Roo.tree.TreePanel = function(el, config){
35273     var root = false;
35274     var loader = false;
35275     if (config.root) {
35276         root = config.root;
35277         delete config.root;
35278     }
35279     if (config.loader) {
35280         loader = config.loader;
35281         delete config.loader;
35282     }
35283     
35284     Roo.apply(this, config);
35285     Roo.tree.TreePanel.superclass.constructor.call(this);
35286     this.el = Roo.get(el);
35287     this.el.addClass('x-tree');
35288     //console.log(root);
35289     if (root) {
35290         this.setRootNode( Roo.factory(root, Roo.tree));
35291     }
35292     if (loader) {
35293         this.loader = Roo.factory(loader, Roo.tree);
35294     }
35295    /**
35296     * Read-only. The id of the container element becomes this TreePanel's id.
35297     */
35298     this.id = this.el.id;
35299     this.addEvents({
35300         /**
35301         * @event beforeload
35302         * Fires before a node is loaded, return false to cancel
35303         * @param {Node} node The node being loaded
35304         */
35305         "beforeload" : true,
35306         /**
35307         * @event load
35308         * Fires when a node is loaded
35309         * @param {Node} node The node that was loaded
35310         */
35311         "load" : true,
35312         /**
35313         * @event textchange
35314         * Fires when the text for a node is changed
35315         * @param {Node} node The node
35316         * @param {String} text The new text
35317         * @param {String} oldText The old text
35318         */
35319         "textchange" : true,
35320         /**
35321         * @event beforeexpand
35322         * Fires before a node is expanded, return false to cancel.
35323         * @param {Node} node The node
35324         * @param {Boolean} deep
35325         * @param {Boolean} anim
35326         */
35327         "beforeexpand" : true,
35328         /**
35329         * @event beforecollapse
35330         * Fires before a node is collapsed, return false to cancel.
35331         * @param {Node} node The node
35332         * @param {Boolean} deep
35333         * @param {Boolean} anim
35334         */
35335         "beforecollapse" : true,
35336         /**
35337         * @event expand
35338         * Fires when a node is expanded
35339         * @param {Node} node The node
35340         */
35341         "expand" : true,
35342         /**
35343         * @event disabledchange
35344         * Fires when the disabled status of a node changes
35345         * @param {Node} node The node
35346         * @param {Boolean} disabled
35347         */
35348         "disabledchange" : true,
35349         /**
35350         * @event collapse
35351         * Fires when a node is collapsed
35352         * @param {Node} node The node
35353         */
35354         "collapse" : true,
35355         /**
35356         * @event beforeclick
35357         * Fires before click processing on a node. Return false to cancel the default action.
35358         * @param {Node} node The node
35359         * @param {Roo.EventObject} e The event object
35360         */
35361         "beforeclick":true,
35362         /**
35363         * @event checkchange
35364         * Fires when a node with a checkbox's checked property changes
35365         * @param {Node} this This node
35366         * @param {Boolean} checked
35367         */
35368         "checkchange":true,
35369         /**
35370         * @event click
35371         * Fires when a node is clicked
35372         * @param {Node} node The node
35373         * @param {Roo.EventObject} e The event object
35374         */
35375         "click":true,
35376         /**
35377         * @event dblclick
35378         * Fires when a node is double clicked
35379         * @param {Node} node The node
35380         * @param {Roo.EventObject} e The event object
35381         */
35382         "dblclick":true,
35383         /**
35384         * @event contextmenu
35385         * Fires when a node is right clicked
35386         * @param {Node} node The node
35387         * @param {Roo.EventObject} e The event object
35388         */
35389         "contextmenu":true,
35390         /**
35391         * @event beforechildrenrendered
35392         * Fires right before the child nodes for a node are rendered
35393         * @param {Node} node The node
35394         */
35395         "beforechildrenrendered":true,
35396         /**
35397         * @event startdrag
35398         * Fires when a node starts being dragged
35399         * @param {Roo.tree.TreePanel} this
35400         * @param {Roo.tree.TreeNode} node
35401         * @param {event} e The raw browser event
35402         */ 
35403        "startdrag" : true,
35404        /**
35405         * @event enddrag
35406         * Fires when a drag operation is complete
35407         * @param {Roo.tree.TreePanel} this
35408         * @param {Roo.tree.TreeNode} node
35409         * @param {event} e The raw browser event
35410         */
35411        "enddrag" : true,
35412        /**
35413         * @event dragdrop
35414         * Fires when a dragged node is dropped on a valid DD target
35415         * @param {Roo.tree.TreePanel} this
35416         * @param {Roo.tree.TreeNode} node
35417         * @param {DD} dd The dd it was dropped on
35418         * @param {event} e The raw browser event
35419         */
35420        "dragdrop" : true,
35421        /**
35422         * @event beforenodedrop
35423         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35424         * passed to handlers has the following properties:<br />
35425         * <ul style="padding:5px;padding-left:16px;">
35426         * <li>tree - The TreePanel</li>
35427         * <li>target - The node being targeted for the drop</li>
35428         * <li>data - The drag data from the drag source</li>
35429         * <li>point - The point of the drop - append, above or below</li>
35430         * <li>source - The drag source</li>
35431         * <li>rawEvent - Raw mouse event</li>
35432         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35433         * to be inserted by setting them on this object.</li>
35434         * <li>cancel - Set this to true to cancel the drop.</li>
35435         * </ul>
35436         * @param {Object} dropEvent
35437         */
35438        "beforenodedrop" : true,
35439        /**
35440         * @event nodedrop
35441         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35442         * passed to handlers has the following properties:<br />
35443         * <ul style="padding:5px;padding-left:16px;">
35444         * <li>tree - The TreePanel</li>
35445         * <li>target - The node being targeted for the drop</li>
35446         * <li>data - The drag data from the drag source</li>
35447         * <li>point - The point of the drop - append, above or below</li>
35448         * <li>source - The drag source</li>
35449         * <li>rawEvent - Raw mouse event</li>
35450         * <li>dropNode - Dropped node(s).</li>
35451         * </ul>
35452         * @param {Object} dropEvent
35453         */
35454        "nodedrop" : true,
35455         /**
35456         * @event nodedragover
35457         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35458         * passed to handlers has the following properties:<br />
35459         * <ul style="padding:5px;padding-left:16px;">
35460         * <li>tree - The TreePanel</li>
35461         * <li>target - The node being targeted for the drop</li>
35462         * <li>data - The drag data from the drag source</li>
35463         * <li>point - The point of the drop - append, above or below</li>
35464         * <li>source - The drag source</li>
35465         * <li>rawEvent - Raw mouse event</li>
35466         * <li>dropNode - Drop node(s) provided by the source.</li>
35467         * <li>cancel - Set this to true to signal drop not allowed.</li>
35468         * </ul>
35469         * @param {Object} dragOverEvent
35470         */
35471        "nodedragover" : true,
35472        /**
35473         * @event appendnode
35474         * Fires when append node to the tree
35475         * @param {Roo.tree.TreePanel} this
35476         * @param {Roo.tree.TreeNode} node
35477         * @param {Number} index The index of the newly appended node
35478         */
35479        "appendnode" : true
35480         
35481     });
35482     if(this.singleExpand){
35483        this.on("beforeexpand", this.restrictExpand, this);
35484     }
35485     if (this.editor) {
35486         this.editor.tree = this;
35487         this.editor = Roo.factory(this.editor, Roo.tree);
35488     }
35489     
35490     if (this.selModel) {
35491         this.selModel = Roo.factory(this.selModel, Roo.tree);
35492     }
35493    
35494 };
35495 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35496     rootVisible : true,
35497     animate: Roo.enableFx,
35498     lines : true,
35499     enableDD : false,
35500     hlDrop : Roo.enableFx,
35501   
35502     renderer: false,
35503     
35504     rendererTip: false,
35505     // private
35506     restrictExpand : function(node){
35507         var p = node.parentNode;
35508         if(p){
35509             if(p.expandedChild && p.expandedChild.parentNode == p){
35510                 p.expandedChild.collapse();
35511             }
35512             p.expandedChild = node;
35513         }
35514     },
35515
35516     // private override
35517     setRootNode : function(node){
35518         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35519         if(!this.rootVisible){
35520             node.ui = new Roo.tree.RootTreeNodeUI(node);
35521         }
35522         return node;
35523     },
35524
35525     /**
35526      * Returns the container element for this TreePanel
35527      */
35528     getEl : function(){
35529         return this.el;
35530     },
35531
35532     /**
35533      * Returns the default TreeLoader for this TreePanel
35534      */
35535     getLoader : function(){
35536         return this.loader;
35537     },
35538
35539     /**
35540      * Expand all nodes
35541      */
35542     expandAll : function(){
35543         this.root.expand(true);
35544     },
35545
35546     /**
35547      * Collapse all nodes
35548      */
35549     collapseAll : function(){
35550         this.root.collapse(true);
35551     },
35552
35553     /**
35554      * Returns the selection model used by this TreePanel
35555      */
35556     getSelectionModel : function(){
35557         if(!this.selModel){
35558             this.selModel = new Roo.tree.DefaultSelectionModel();
35559         }
35560         return this.selModel;
35561     },
35562
35563     /**
35564      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35565      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35566      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35567      * @return {Array}
35568      */
35569     getChecked : function(a, startNode){
35570         startNode = startNode || this.root;
35571         var r = [];
35572         var f = function(){
35573             if(this.attributes.checked){
35574                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35575             }
35576         }
35577         startNode.cascade(f);
35578         return r;
35579     },
35580
35581     /**
35582      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35583      * @param {String} path
35584      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35585      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35586      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35587      */
35588     expandPath : function(path, attr, callback){
35589         attr = attr || "id";
35590         var keys = path.split(this.pathSeparator);
35591         var curNode = this.root;
35592         if(curNode.attributes[attr] != keys[1]){ // invalid root
35593             if(callback){
35594                 callback(false, null);
35595             }
35596             return;
35597         }
35598         var index = 1;
35599         var f = function(){
35600             if(++index == keys.length){
35601                 if(callback){
35602                     callback(true, curNode);
35603                 }
35604                 return;
35605             }
35606             var c = curNode.findChild(attr, keys[index]);
35607             if(!c){
35608                 if(callback){
35609                     callback(false, curNode);
35610                 }
35611                 return;
35612             }
35613             curNode = c;
35614             c.expand(false, false, f);
35615         };
35616         curNode.expand(false, false, f);
35617     },
35618
35619     /**
35620      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35621      * @param {String} path
35622      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35623      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35624      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35625      */
35626     selectPath : function(path, attr, callback){
35627         attr = attr || "id";
35628         var keys = path.split(this.pathSeparator);
35629         var v = keys.pop();
35630         if(keys.length > 0){
35631             var f = function(success, node){
35632                 if(success && node){
35633                     var n = node.findChild(attr, v);
35634                     if(n){
35635                         n.select();
35636                         if(callback){
35637                             callback(true, n);
35638                         }
35639                     }else if(callback){
35640                         callback(false, n);
35641                     }
35642                 }else{
35643                     if(callback){
35644                         callback(false, n);
35645                     }
35646                 }
35647             };
35648             this.expandPath(keys.join(this.pathSeparator), attr, f);
35649         }else{
35650             this.root.select();
35651             if(callback){
35652                 callback(true, this.root);
35653             }
35654         }
35655     },
35656
35657     getTreeEl : function(){
35658         return this.el;
35659     },
35660
35661     /**
35662      * Trigger rendering of this TreePanel
35663      */
35664     render : function(){
35665         if (this.innerCt) {
35666             return this; // stop it rendering more than once!!
35667         }
35668         
35669         this.innerCt = this.el.createChild({tag:"ul",
35670                cls:"x-tree-root-ct " +
35671                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35672
35673         if(this.containerScroll){
35674             Roo.dd.ScrollManager.register(this.el);
35675         }
35676         if((this.enableDD || this.enableDrop) && !this.dropZone){
35677            /**
35678             * The dropZone used by this tree if drop is enabled
35679             * @type Roo.tree.TreeDropZone
35680             */
35681              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35682                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35683            });
35684         }
35685         if((this.enableDD || this.enableDrag) && !this.dragZone){
35686            /**
35687             * The dragZone used by this tree if drag is enabled
35688             * @type Roo.tree.TreeDragZone
35689             */
35690             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35691                ddGroup: this.ddGroup || "TreeDD",
35692                scroll: this.ddScroll
35693            });
35694         }
35695         this.getSelectionModel().init(this);
35696         if (!this.root) {
35697             Roo.log("ROOT not set in tree");
35698             return this;
35699         }
35700         this.root.render();
35701         if(!this.rootVisible){
35702             this.root.renderChildren();
35703         }
35704         return this;
35705     }
35706 });/*
35707  * Based on:
35708  * Ext JS Library 1.1.1
35709  * Copyright(c) 2006-2007, Ext JS, LLC.
35710  *
35711  * Originally Released Under LGPL - original licence link has changed is not relivant.
35712  *
35713  * Fork - LGPL
35714  * <script type="text/javascript">
35715  */
35716  
35717
35718 /**
35719  * @class Roo.tree.DefaultSelectionModel
35720  * @extends Roo.util.Observable
35721  * The default single selection for a TreePanel.
35722  * @param {Object} cfg Configuration
35723  */
35724 Roo.tree.DefaultSelectionModel = function(cfg){
35725    this.selNode = null;
35726    
35727    
35728    
35729    this.addEvents({
35730        /**
35731         * @event selectionchange
35732         * Fires when the selected node changes
35733         * @param {DefaultSelectionModel} this
35734         * @param {TreeNode} node the new selection
35735         */
35736        "selectionchange" : true,
35737
35738        /**
35739         * @event beforeselect
35740         * Fires before the selected node changes, return false to cancel the change
35741         * @param {DefaultSelectionModel} this
35742         * @param {TreeNode} node the new selection
35743         * @param {TreeNode} node the old selection
35744         */
35745        "beforeselect" : true
35746    });
35747    
35748     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35749 };
35750
35751 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35752     init : function(tree){
35753         this.tree = tree;
35754         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35755         tree.on("click", this.onNodeClick, this);
35756     },
35757     
35758     onNodeClick : function(node, e){
35759         if (e.ctrlKey && this.selNode == node)  {
35760             this.unselect(node);
35761             return;
35762         }
35763         this.select(node);
35764     },
35765     
35766     /**
35767      * Select a node.
35768      * @param {TreeNode} node The node to select
35769      * @return {TreeNode} The selected node
35770      */
35771     select : function(node){
35772         var last = this.selNode;
35773         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35774             if(last){
35775                 last.ui.onSelectedChange(false);
35776             }
35777             this.selNode = node;
35778             node.ui.onSelectedChange(true);
35779             this.fireEvent("selectionchange", this, node, last);
35780         }
35781         return node;
35782     },
35783     
35784     /**
35785      * Deselect a node.
35786      * @param {TreeNode} node The node to unselect
35787      */
35788     unselect : function(node){
35789         if(this.selNode == node){
35790             this.clearSelections();
35791         }    
35792     },
35793     
35794     /**
35795      * Clear all selections
35796      */
35797     clearSelections : function(){
35798         var n = this.selNode;
35799         if(n){
35800             n.ui.onSelectedChange(false);
35801             this.selNode = null;
35802             this.fireEvent("selectionchange", this, null);
35803         }
35804         return n;
35805     },
35806     
35807     /**
35808      * Get the selected node
35809      * @return {TreeNode} The selected node
35810      */
35811     getSelectedNode : function(){
35812         return this.selNode;    
35813     },
35814     
35815     /**
35816      * Returns true if the node is selected
35817      * @param {TreeNode} node The node to check
35818      * @return {Boolean}
35819      */
35820     isSelected : function(node){
35821         return this.selNode == node;  
35822     },
35823
35824     /**
35825      * Selects the node above the selected node in the tree, intelligently walking the nodes
35826      * @return TreeNode The new selection
35827      */
35828     selectPrevious : function(){
35829         var s = this.selNode || this.lastSelNode;
35830         if(!s){
35831             return null;
35832         }
35833         var ps = s.previousSibling;
35834         if(ps){
35835             if(!ps.isExpanded() || ps.childNodes.length < 1){
35836                 return this.select(ps);
35837             } else{
35838                 var lc = ps.lastChild;
35839                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35840                     lc = lc.lastChild;
35841                 }
35842                 return this.select(lc);
35843             }
35844         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35845             return this.select(s.parentNode);
35846         }
35847         return null;
35848     },
35849
35850     /**
35851      * Selects the node above the selected node in the tree, intelligently walking the nodes
35852      * @return TreeNode The new selection
35853      */
35854     selectNext : function(){
35855         var s = this.selNode || this.lastSelNode;
35856         if(!s){
35857             return null;
35858         }
35859         if(s.firstChild && s.isExpanded()){
35860              return this.select(s.firstChild);
35861          }else if(s.nextSibling){
35862              return this.select(s.nextSibling);
35863          }else if(s.parentNode){
35864             var newS = null;
35865             s.parentNode.bubble(function(){
35866                 if(this.nextSibling){
35867                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35868                     return false;
35869                 }
35870             });
35871             return newS;
35872          }
35873         return null;
35874     },
35875
35876     onKeyDown : function(e){
35877         var s = this.selNode || this.lastSelNode;
35878         // undesirable, but required
35879         var sm = this;
35880         if(!s){
35881             return;
35882         }
35883         var k = e.getKey();
35884         switch(k){
35885              case e.DOWN:
35886                  e.stopEvent();
35887                  this.selectNext();
35888              break;
35889              case e.UP:
35890                  e.stopEvent();
35891                  this.selectPrevious();
35892              break;
35893              case e.RIGHT:
35894                  e.preventDefault();
35895                  if(s.hasChildNodes()){
35896                      if(!s.isExpanded()){
35897                          s.expand();
35898                      }else if(s.firstChild){
35899                          this.select(s.firstChild, e);
35900                      }
35901                  }
35902              break;
35903              case e.LEFT:
35904                  e.preventDefault();
35905                  if(s.hasChildNodes() && s.isExpanded()){
35906                      s.collapse();
35907                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35908                      this.select(s.parentNode, e);
35909                  }
35910              break;
35911         };
35912     }
35913 });
35914
35915 /**
35916  * @class Roo.tree.MultiSelectionModel
35917  * @extends Roo.util.Observable
35918  * Multi selection for a TreePanel.
35919  * @param {Object} cfg Configuration
35920  */
35921 Roo.tree.MultiSelectionModel = function(){
35922    this.selNodes = [];
35923    this.selMap = {};
35924    this.addEvents({
35925        /**
35926         * @event selectionchange
35927         * Fires when the selected nodes change
35928         * @param {MultiSelectionModel} this
35929         * @param {Array} nodes Array of the selected nodes
35930         */
35931        "selectionchange" : true
35932    });
35933    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35934    
35935 };
35936
35937 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35938     init : function(tree){
35939         this.tree = tree;
35940         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35941         tree.on("click", this.onNodeClick, this);
35942     },
35943     
35944     onNodeClick : function(node, e){
35945         this.select(node, e, e.ctrlKey);
35946     },
35947     
35948     /**
35949      * Select a node.
35950      * @param {TreeNode} node The node to select
35951      * @param {EventObject} e (optional) An event associated with the selection
35952      * @param {Boolean} keepExisting True to retain existing selections
35953      * @return {TreeNode} The selected node
35954      */
35955     select : function(node, e, keepExisting){
35956         if(keepExisting !== true){
35957             this.clearSelections(true);
35958         }
35959         if(this.isSelected(node)){
35960             this.lastSelNode = node;
35961             return node;
35962         }
35963         this.selNodes.push(node);
35964         this.selMap[node.id] = node;
35965         this.lastSelNode = node;
35966         node.ui.onSelectedChange(true);
35967         this.fireEvent("selectionchange", this, this.selNodes);
35968         return node;
35969     },
35970     
35971     /**
35972      * Deselect a node.
35973      * @param {TreeNode} node The node to unselect
35974      */
35975     unselect : function(node){
35976         if(this.selMap[node.id]){
35977             node.ui.onSelectedChange(false);
35978             var sn = this.selNodes;
35979             var index = -1;
35980             if(sn.indexOf){
35981                 index = sn.indexOf(node);
35982             }else{
35983                 for(var i = 0, len = sn.length; i < len; i++){
35984                     if(sn[i] == node){
35985                         index = i;
35986                         break;
35987                     }
35988                 }
35989             }
35990             if(index != -1){
35991                 this.selNodes.splice(index, 1);
35992             }
35993             delete this.selMap[node.id];
35994             this.fireEvent("selectionchange", this, this.selNodes);
35995         }
35996     },
35997     
35998     /**
35999      * Clear all selections
36000      */
36001     clearSelections : function(suppressEvent){
36002         var sn = this.selNodes;
36003         if(sn.length > 0){
36004             for(var i = 0, len = sn.length; i < len; i++){
36005                 sn[i].ui.onSelectedChange(false);
36006             }
36007             this.selNodes = [];
36008             this.selMap = {};
36009             if(suppressEvent !== true){
36010                 this.fireEvent("selectionchange", this, this.selNodes);
36011             }
36012         }
36013     },
36014     
36015     /**
36016      * Returns true if the node is selected
36017      * @param {TreeNode} node The node to check
36018      * @return {Boolean}
36019      */
36020     isSelected : function(node){
36021         return this.selMap[node.id] ? true : false;  
36022     },
36023     
36024     /**
36025      * Returns an array of the selected nodes
36026      * @return {Array}
36027      */
36028     getSelectedNodes : function(){
36029         return this.selNodes;    
36030     },
36031
36032     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36033
36034     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36035
36036     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36037 });/*
36038  * Based on:
36039  * Ext JS Library 1.1.1
36040  * Copyright(c) 2006-2007, Ext JS, LLC.
36041  *
36042  * Originally Released Under LGPL - original licence link has changed is not relivant.
36043  *
36044  * Fork - LGPL
36045  * <script type="text/javascript">
36046  */
36047  
36048 /**
36049  * @class Roo.tree.TreeNode
36050  * @extends Roo.data.Node
36051  * @cfg {String} text The text for this node
36052  * @cfg {Boolean} expanded true to start the node expanded
36053  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36054  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36055  * @cfg {Boolean} disabled true to start the node disabled
36056  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36057  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36058  * @cfg {String} cls A css class to be added to the node
36059  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36060  * @cfg {String} href URL of the link used for the node (defaults to #)
36061  * @cfg {String} hrefTarget target frame for the link
36062  * @cfg {String} qtip An Ext QuickTip for the node
36063  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36064  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36065  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36066  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36067  * (defaults to undefined with no checkbox rendered)
36068  * @constructor
36069  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36070  */
36071 Roo.tree.TreeNode = function(attributes){
36072     attributes = attributes || {};
36073     if(typeof attributes == "string"){
36074         attributes = {text: attributes};
36075     }
36076     this.childrenRendered = false;
36077     this.rendered = false;
36078     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36079     this.expanded = attributes.expanded === true;
36080     this.isTarget = attributes.isTarget !== false;
36081     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36082     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36083
36084     /**
36085      * Read-only. The text for this node. To change it use setText().
36086      * @type String
36087      */
36088     this.text = attributes.text;
36089     /**
36090      * True if this node is disabled.
36091      * @type Boolean
36092      */
36093     this.disabled = attributes.disabled === true;
36094
36095     this.addEvents({
36096         /**
36097         * @event textchange
36098         * Fires when the text for this node is changed
36099         * @param {Node} this This node
36100         * @param {String} text The new text
36101         * @param {String} oldText The old text
36102         */
36103         "textchange" : true,
36104         /**
36105         * @event beforeexpand
36106         * Fires before this node is expanded, return false to cancel.
36107         * @param {Node} this This node
36108         * @param {Boolean} deep
36109         * @param {Boolean} anim
36110         */
36111         "beforeexpand" : true,
36112         /**
36113         * @event beforecollapse
36114         * Fires before this node is collapsed, return false to cancel.
36115         * @param {Node} this This node
36116         * @param {Boolean} deep
36117         * @param {Boolean} anim
36118         */
36119         "beforecollapse" : true,
36120         /**
36121         * @event expand
36122         * Fires when this node is expanded
36123         * @param {Node} this This node
36124         */
36125         "expand" : true,
36126         /**
36127         * @event disabledchange
36128         * Fires when the disabled status of this node changes
36129         * @param {Node} this This node
36130         * @param {Boolean} disabled
36131         */
36132         "disabledchange" : true,
36133         /**
36134         * @event collapse
36135         * Fires when this node is collapsed
36136         * @param {Node} this This node
36137         */
36138         "collapse" : true,
36139         /**
36140         * @event beforeclick
36141         * Fires before click processing. Return false to cancel the default action.
36142         * @param {Node} this This node
36143         * @param {Roo.EventObject} e The event object
36144         */
36145         "beforeclick":true,
36146         /**
36147         * @event checkchange
36148         * Fires when a node with a checkbox's checked property changes
36149         * @param {Node} this This node
36150         * @param {Boolean} checked
36151         */
36152         "checkchange":true,
36153         /**
36154         * @event click
36155         * Fires when this node is clicked
36156         * @param {Node} this This node
36157         * @param {Roo.EventObject} e The event object
36158         */
36159         "click":true,
36160         /**
36161         * @event dblclick
36162         * Fires when this node is double clicked
36163         * @param {Node} this This node
36164         * @param {Roo.EventObject} e The event object
36165         */
36166         "dblclick":true,
36167         /**
36168         * @event contextmenu
36169         * Fires when this node is right clicked
36170         * @param {Node} this This node
36171         * @param {Roo.EventObject} e The event object
36172         */
36173         "contextmenu":true,
36174         /**
36175         * @event beforechildrenrendered
36176         * Fires right before the child nodes for this node are rendered
36177         * @param {Node} this This node
36178         */
36179         "beforechildrenrendered":true
36180     });
36181
36182     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36183
36184     /**
36185      * Read-only. The UI for this node
36186      * @type TreeNodeUI
36187      */
36188     this.ui = new uiClass(this);
36189     
36190     // finally support items[]
36191     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36192         return;
36193     }
36194     
36195     
36196     Roo.each(this.attributes.items, function(c) {
36197         this.appendChild(Roo.factory(c,Roo.Tree));
36198     }, this);
36199     delete this.attributes.items;
36200     
36201     
36202     
36203 };
36204 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36205     preventHScroll: true,
36206     /**
36207      * Returns true if this node is expanded
36208      * @return {Boolean}
36209      */
36210     isExpanded : function(){
36211         return this.expanded;
36212     },
36213
36214     /**
36215      * Returns the UI object for this node
36216      * @return {TreeNodeUI}
36217      */
36218     getUI : function(){
36219         return this.ui;
36220     },
36221
36222     // private override
36223     setFirstChild : function(node){
36224         var of = this.firstChild;
36225         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36226         if(this.childrenRendered && of && node != of){
36227             of.renderIndent(true, true);
36228         }
36229         if(this.rendered){
36230             this.renderIndent(true, true);
36231         }
36232     },
36233
36234     // private override
36235     setLastChild : function(node){
36236         var ol = this.lastChild;
36237         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36238         if(this.childrenRendered && ol && node != ol){
36239             ol.renderIndent(true, true);
36240         }
36241         if(this.rendered){
36242             this.renderIndent(true, true);
36243         }
36244     },
36245
36246     // these methods are overridden to provide lazy rendering support
36247     // private override
36248     appendChild : function()
36249     {
36250         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36251         if(node && this.childrenRendered){
36252             node.render();
36253         }
36254         this.ui.updateExpandIcon();
36255         return node;
36256     },
36257
36258     // private override
36259     removeChild : function(node){
36260         this.ownerTree.getSelectionModel().unselect(node);
36261         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36262         // if it's been rendered remove dom node
36263         if(this.childrenRendered){
36264             node.ui.remove();
36265         }
36266         if(this.childNodes.length < 1){
36267             this.collapse(false, false);
36268         }else{
36269             this.ui.updateExpandIcon();
36270         }
36271         if(!this.firstChild) {
36272             this.childrenRendered = false;
36273         }
36274         return node;
36275     },
36276
36277     // private override
36278     insertBefore : function(node, refNode){
36279         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36280         if(newNode && refNode && this.childrenRendered){
36281             node.render();
36282         }
36283         this.ui.updateExpandIcon();
36284         return newNode;
36285     },
36286
36287     /**
36288      * Sets the text for this node
36289      * @param {String} text
36290      */
36291     setText : function(text){
36292         var oldText = this.text;
36293         this.text = text;
36294         this.attributes.text = text;
36295         if(this.rendered){ // event without subscribing
36296             this.ui.onTextChange(this, text, oldText);
36297         }
36298         this.fireEvent("textchange", this, text, oldText);
36299     },
36300
36301     /**
36302      * Triggers selection of this node
36303      */
36304     select : function(){
36305         this.getOwnerTree().getSelectionModel().select(this);
36306     },
36307
36308     /**
36309      * Triggers deselection of this node
36310      */
36311     unselect : function(){
36312         this.getOwnerTree().getSelectionModel().unselect(this);
36313     },
36314
36315     /**
36316      * Returns true if this node is selected
36317      * @return {Boolean}
36318      */
36319     isSelected : function(){
36320         return this.getOwnerTree().getSelectionModel().isSelected(this);
36321     },
36322
36323     /**
36324      * Expand this node.
36325      * @param {Boolean} deep (optional) True to expand all children as well
36326      * @param {Boolean} anim (optional) false to cancel the default animation
36327      * @param {Function} callback (optional) A callback to be called when
36328      * expanding this node completes (does not wait for deep expand to complete).
36329      * Called with 1 parameter, this node.
36330      */
36331     expand : function(deep, anim, callback){
36332         if(!this.expanded){
36333             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36334                 return;
36335             }
36336             if(!this.childrenRendered){
36337                 this.renderChildren();
36338             }
36339             this.expanded = true;
36340             
36341             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36342                 this.ui.animExpand(function(){
36343                     this.fireEvent("expand", this);
36344                     if(typeof callback == "function"){
36345                         callback(this);
36346                     }
36347                     if(deep === true){
36348                         this.expandChildNodes(true);
36349                     }
36350                 }.createDelegate(this));
36351                 return;
36352             }else{
36353                 this.ui.expand();
36354                 this.fireEvent("expand", this);
36355                 if(typeof callback == "function"){
36356                     callback(this);
36357                 }
36358             }
36359         }else{
36360            if(typeof callback == "function"){
36361                callback(this);
36362            }
36363         }
36364         if(deep === true){
36365             this.expandChildNodes(true);
36366         }
36367     },
36368
36369     isHiddenRoot : function(){
36370         return this.isRoot && !this.getOwnerTree().rootVisible;
36371     },
36372
36373     /**
36374      * Collapse this node.
36375      * @param {Boolean} deep (optional) True to collapse all children as well
36376      * @param {Boolean} anim (optional) false to cancel the default animation
36377      */
36378     collapse : function(deep, anim){
36379         if(this.expanded && !this.isHiddenRoot()){
36380             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36381                 return;
36382             }
36383             this.expanded = false;
36384             if((this.getOwnerTree().animate && anim !== false) || anim){
36385                 this.ui.animCollapse(function(){
36386                     this.fireEvent("collapse", this);
36387                     if(deep === true){
36388                         this.collapseChildNodes(true);
36389                     }
36390                 }.createDelegate(this));
36391                 return;
36392             }else{
36393                 this.ui.collapse();
36394                 this.fireEvent("collapse", this);
36395             }
36396         }
36397         if(deep === true){
36398             var cs = this.childNodes;
36399             for(var i = 0, len = cs.length; i < len; i++) {
36400                 cs[i].collapse(true, false);
36401             }
36402         }
36403     },
36404
36405     // private
36406     delayedExpand : function(delay){
36407         if(!this.expandProcId){
36408             this.expandProcId = this.expand.defer(delay, this);
36409         }
36410     },
36411
36412     // private
36413     cancelExpand : function(){
36414         if(this.expandProcId){
36415             clearTimeout(this.expandProcId);
36416         }
36417         this.expandProcId = false;
36418     },
36419
36420     /**
36421      * Toggles expanded/collapsed state of the node
36422      */
36423     toggle : function(){
36424         if(this.expanded){
36425             this.collapse();
36426         }else{
36427             this.expand();
36428         }
36429     },
36430
36431     /**
36432      * Ensures all parent nodes are expanded
36433      */
36434     ensureVisible : function(callback){
36435         var tree = this.getOwnerTree();
36436         tree.expandPath(this.parentNode.getPath(), false, function(){
36437             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36438             Roo.callback(callback);
36439         }.createDelegate(this));
36440     },
36441
36442     /**
36443      * Expand all child nodes
36444      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36445      */
36446     expandChildNodes : function(deep){
36447         var cs = this.childNodes;
36448         for(var i = 0, len = cs.length; i < len; i++) {
36449                 cs[i].expand(deep);
36450         }
36451     },
36452
36453     /**
36454      * Collapse all child nodes
36455      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36456      */
36457     collapseChildNodes : function(deep){
36458         var cs = this.childNodes;
36459         for(var i = 0, len = cs.length; i < len; i++) {
36460                 cs[i].collapse(deep);
36461         }
36462     },
36463
36464     /**
36465      * Disables this node
36466      */
36467     disable : function(){
36468         this.disabled = true;
36469         this.unselect();
36470         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36471             this.ui.onDisableChange(this, true);
36472         }
36473         this.fireEvent("disabledchange", this, true);
36474     },
36475
36476     /**
36477      * Enables this node
36478      */
36479     enable : function(){
36480         this.disabled = false;
36481         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36482             this.ui.onDisableChange(this, false);
36483         }
36484         this.fireEvent("disabledchange", this, false);
36485     },
36486
36487     // private
36488     renderChildren : function(suppressEvent){
36489         if(suppressEvent !== false){
36490             this.fireEvent("beforechildrenrendered", this);
36491         }
36492         var cs = this.childNodes;
36493         for(var i = 0, len = cs.length; i < len; i++){
36494             cs[i].render(true);
36495         }
36496         this.childrenRendered = true;
36497     },
36498
36499     // private
36500     sort : function(fn, scope){
36501         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36502         if(this.childrenRendered){
36503             var cs = this.childNodes;
36504             for(var i = 0, len = cs.length; i < len; i++){
36505                 cs[i].render(true);
36506             }
36507         }
36508     },
36509
36510     // private
36511     render : function(bulkRender){
36512         this.ui.render(bulkRender);
36513         if(!this.rendered){
36514             this.rendered = true;
36515             if(this.expanded){
36516                 this.expanded = false;
36517                 this.expand(false, false);
36518             }
36519         }
36520     },
36521
36522     // private
36523     renderIndent : function(deep, refresh){
36524         if(refresh){
36525             this.ui.childIndent = null;
36526         }
36527         this.ui.renderIndent();
36528         if(deep === true && this.childrenRendered){
36529             var cs = this.childNodes;
36530             for(var i = 0, len = cs.length; i < len; i++){
36531                 cs[i].renderIndent(true, refresh);
36532             }
36533         }
36534     }
36535 });/*
36536  * Based on:
36537  * Ext JS Library 1.1.1
36538  * Copyright(c) 2006-2007, Ext JS, LLC.
36539  *
36540  * Originally Released Under LGPL - original licence link has changed is not relivant.
36541  *
36542  * Fork - LGPL
36543  * <script type="text/javascript">
36544  */
36545  
36546 /**
36547  * @class Roo.tree.AsyncTreeNode
36548  * @extends Roo.tree.TreeNode
36549  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36550  * @constructor
36551  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36552  */
36553  Roo.tree.AsyncTreeNode = function(config){
36554     this.loaded = false;
36555     this.loading = false;
36556     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36557     /**
36558     * @event beforeload
36559     * Fires before this node is loaded, return false to cancel
36560     * @param {Node} this This node
36561     */
36562     this.addEvents({'beforeload':true, 'load': true});
36563     /**
36564     * @event load
36565     * Fires when this node is loaded
36566     * @param {Node} this This node
36567     */
36568     /**
36569      * The loader used by this node (defaults to using the tree's defined loader)
36570      * @type TreeLoader
36571      * @property loader
36572      */
36573 };
36574 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36575     expand : function(deep, anim, callback){
36576         if(this.loading){ // if an async load is already running, waiting til it's done
36577             var timer;
36578             var f = function(){
36579                 if(!this.loading){ // done loading
36580                     clearInterval(timer);
36581                     this.expand(deep, anim, callback);
36582                 }
36583             }.createDelegate(this);
36584             timer = setInterval(f, 200);
36585             return;
36586         }
36587         if(!this.loaded){
36588             if(this.fireEvent("beforeload", this) === false){
36589                 return;
36590             }
36591             this.loading = true;
36592             this.ui.beforeLoad(this);
36593             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36594             if(loader){
36595                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36596                 return;
36597             }
36598         }
36599         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36600     },
36601     
36602     /**
36603      * Returns true if this node is currently loading
36604      * @return {Boolean}
36605      */
36606     isLoading : function(){
36607         return this.loading;  
36608     },
36609     
36610     loadComplete : function(deep, anim, callback){
36611         this.loading = false;
36612         this.loaded = true;
36613         this.ui.afterLoad(this);
36614         this.fireEvent("load", this);
36615         this.expand(deep, anim, callback);
36616     },
36617     
36618     /**
36619      * Returns true if this node has been loaded
36620      * @return {Boolean}
36621      */
36622     isLoaded : function(){
36623         return this.loaded;
36624     },
36625     
36626     hasChildNodes : function(){
36627         if(!this.isLeaf() && !this.loaded){
36628             return true;
36629         }else{
36630             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36631         }
36632     },
36633
36634     /**
36635      * Trigger a reload for this node
36636      * @param {Function} callback
36637      */
36638     reload : function(callback){
36639         this.collapse(false, false);
36640         while(this.firstChild){
36641             this.removeChild(this.firstChild);
36642         }
36643         this.childrenRendered = false;
36644         this.loaded = false;
36645         if(this.isHiddenRoot()){
36646             this.expanded = false;
36647         }
36648         this.expand(false, false, callback);
36649     }
36650 });/*
36651  * Based on:
36652  * Ext JS Library 1.1.1
36653  * Copyright(c) 2006-2007, Ext JS, LLC.
36654  *
36655  * Originally Released Under LGPL - original licence link has changed is not relivant.
36656  *
36657  * Fork - LGPL
36658  * <script type="text/javascript">
36659  */
36660  
36661 /**
36662  * @class Roo.tree.TreeNodeUI
36663  * @constructor
36664  * @param {Object} node The node to render
36665  * The TreeNode UI implementation is separate from the
36666  * tree implementation. Unless you are customizing the tree UI,
36667  * you should never have to use this directly.
36668  */
36669 Roo.tree.TreeNodeUI = function(node){
36670     this.node = node;
36671     this.rendered = false;
36672     this.animating = false;
36673     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36674 };
36675
36676 Roo.tree.TreeNodeUI.prototype = {
36677     removeChild : function(node){
36678         if(this.rendered){
36679             this.ctNode.removeChild(node.ui.getEl());
36680         }
36681     },
36682
36683     beforeLoad : function(){
36684          this.addClass("x-tree-node-loading");
36685     },
36686
36687     afterLoad : function(){
36688          this.removeClass("x-tree-node-loading");
36689     },
36690
36691     onTextChange : function(node, text, oldText){
36692         if(this.rendered){
36693             this.textNode.innerHTML = text;
36694         }
36695     },
36696
36697     onDisableChange : function(node, state){
36698         this.disabled = state;
36699         if(state){
36700             this.addClass("x-tree-node-disabled");
36701         }else{
36702             this.removeClass("x-tree-node-disabled");
36703         }
36704     },
36705
36706     onSelectedChange : function(state){
36707         if(state){
36708             this.focus();
36709             this.addClass("x-tree-selected");
36710         }else{
36711             //this.blur();
36712             this.removeClass("x-tree-selected");
36713         }
36714     },
36715
36716     onMove : function(tree, node, oldParent, newParent, index, refNode){
36717         this.childIndent = null;
36718         if(this.rendered){
36719             var targetNode = newParent.ui.getContainer();
36720             if(!targetNode){//target not rendered
36721                 this.holder = document.createElement("div");
36722                 this.holder.appendChild(this.wrap);
36723                 return;
36724             }
36725             var insertBefore = refNode ? refNode.ui.getEl() : null;
36726             if(insertBefore){
36727                 targetNode.insertBefore(this.wrap, insertBefore);
36728             }else{
36729                 targetNode.appendChild(this.wrap);
36730             }
36731             this.node.renderIndent(true);
36732         }
36733     },
36734
36735     addClass : function(cls){
36736         if(this.elNode){
36737             Roo.fly(this.elNode).addClass(cls);
36738         }
36739     },
36740
36741     removeClass : function(cls){
36742         if(this.elNode){
36743             Roo.fly(this.elNode).removeClass(cls);
36744         }
36745     },
36746
36747     remove : function(){
36748         if(this.rendered){
36749             this.holder = document.createElement("div");
36750             this.holder.appendChild(this.wrap);
36751         }
36752     },
36753
36754     fireEvent : function(){
36755         return this.node.fireEvent.apply(this.node, arguments);
36756     },
36757
36758     initEvents : function(){
36759         this.node.on("move", this.onMove, this);
36760         var E = Roo.EventManager;
36761         var a = this.anchor;
36762
36763         var el = Roo.fly(a, '_treeui');
36764
36765         if(Roo.isOpera){ // opera render bug ignores the CSS
36766             el.setStyle("text-decoration", "none");
36767         }
36768
36769         el.on("click", this.onClick, this);
36770         el.on("dblclick", this.onDblClick, this);
36771
36772         if(this.checkbox){
36773             Roo.EventManager.on(this.checkbox,
36774                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36775         }
36776
36777         el.on("contextmenu", this.onContextMenu, this);
36778
36779         var icon = Roo.fly(this.iconNode);
36780         icon.on("click", this.onClick, this);
36781         icon.on("dblclick", this.onDblClick, this);
36782         icon.on("contextmenu", this.onContextMenu, this);
36783         E.on(this.ecNode, "click", this.ecClick, this, true);
36784
36785         if(this.node.disabled){
36786             this.addClass("x-tree-node-disabled");
36787         }
36788         if(this.node.hidden){
36789             this.addClass("x-tree-node-disabled");
36790         }
36791         var ot = this.node.getOwnerTree();
36792         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36793         if(dd && (!this.node.isRoot || ot.rootVisible)){
36794             Roo.dd.Registry.register(this.elNode, {
36795                 node: this.node,
36796                 handles: this.getDDHandles(),
36797                 isHandle: false
36798             });
36799         }
36800     },
36801
36802     getDDHandles : function(){
36803         return [this.iconNode, this.textNode];
36804     },
36805
36806     hide : function(){
36807         if(this.rendered){
36808             this.wrap.style.display = "none";
36809         }
36810     },
36811
36812     show : function(){
36813         if(this.rendered){
36814             this.wrap.style.display = "";
36815         }
36816     },
36817
36818     onContextMenu : function(e){
36819         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36820             e.preventDefault();
36821             this.focus();
36822             this.fireEvent("contextmenu", this.node, e);
36823         }
36824     },
36825
36826     onClick : function(e){
36827         if(this.dropping){
36828             e.stopEvent();
36829             return;
36830         }
36831         if(this.fireEvent("beforeclick", this.node, e) !== false){
36832             if(!this.disabled && this.node.attributes.href){
36833                 this.fireEvent("click", this.node, e);
36834                 return;
36835             }
36836             e.preventDefault();
36837             if(this.disabled){
36838                 return;
36839             }
36840
36841             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36842                 this.node.toggle();
36843             }
36844
36845             this.fireEvent("click", this.node, e);
36846         }else{
36847             e.stopEvent();
36848         }
36849     },
36850
36851     onDblClick : function(e){
36852         e.preventDefault();
36853         if(this.disabled){
36854             return;
36855         }
36856         if(this.checkbox){
36857             this.toggleCheck();
36858         }
36859         if(!this.animating && this.node.hasChildNodes()){
36860             this.node.toggle();
36861         }
36862         this.fireEvent("dblclick", this.node, e);
36863     },
36864
36865     onCheckChange : function(){
36866         var checked = this.checkbox.checked;
36867         this.node.attributes.checked = checked;
36868         this.fireEvent('checkchange', this.node, checked);
36869     },
36870
36871     ecClick : function(e){
36872         if(!this.animating && this.node.hasChildNodes()){
36873             this.node.toggle();
36874         }
36875     },
36876
36877     startDrop : function(){
36878         this.dropping = true;
36879     },
36880
36881     // delayed drop so the click event doesn't get fired on a drop
36882     endDrop : function(){
36883        setTimeout(function(){
36884            this.dropping = false;
36885        }.createDelegate(this), 50);
36886     },
36887
36888     expand : function(){
36889         this.updateExpandIcon();
36890         this.ctNode.style.display = "";
36891     },
36892
36893     focus : function(){
36894         if(!this.node.preventHScroll){
36895             try{this.anchor.focus();
36896             }catch(e){}
36897         }else if(!Roo.isIE){
36898             try{
36899                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36900                 var l = noscroll.scrollLeft;
36901                 this.anchor.focus();
36902                 noscroll.scrollLeft = l;
36903             }catch(e){}
36904         }
36905     },
36906
36907     toggleCheck : function(value){
36908         var cb = this.checkbox;
36909         if(cb){
36910             cb.checked = (value === undefined ? !cb.checked : value);
36911         }
36912     },
36913
36914     blur : function(){
36915         try{
36916             this.anchor.blur();
36917         }catch(e){}
36918     },
36919
36920     animExpand : function(callback){
36921         var ct = Roo.get(this.ctNode);
36922         ct.stopFx();
36923         if(!this.node.hasChildNodes()){
36924             this.updateExpandIcon();
36925             this.ctNode.style.display = "";
36926             Roo.callback(callback);
36927             return;
36928         }
36929         this.animating = true;
36930         this.updateExpandIcon();
36931
36932         ct.slideIn('t', {
36933            callback : function(){
36934                this.animating = false;
36935                Roo.callback(callback);
36936             },
36937             scope: this,
36938             duration: this.node.ownerTree.duration || .25
36939         });
36940     },
36941
36942     highlight : function(){
36943         var tree = this.node.getOwnerTree();
36944         Roo.fly(this.wrap).highlight(
36945             tree.hlColor || "C3DAF9",
36946             {endColor: tree.hlBaseColor}
36947         );
36948     },
36949
36950     collapse : function(){
36951         this.updateExpandIcon();
36952         this.ctNode.style.display = "none";
36953     },
36954
36955     animCollapse : function(callback){
36956         var ct = Roo.get(this.ctNode);
36957         ct.enableDisplayMode('block');
36958         ct.stopFx();
36959
36960         this.animating = true;
36961         this.updateExpandIcon();
36962
36963         ct.slideOut('t', {
36964             callback : function(){
36965                this.animating = false;
36966                Roo.callback(callback);
36967             },
36968             scope: this,
36969             duration: this.node.ownerTree.duration || .25
36970         });
36971     },
36972
36973     getContainer : function(){
36974         return this.ctNode;
36975     },
36976
36977     getEl : function(){
36978         return this.wrap;
36979     },
36980
36981     appendDDGhost : function(ghostNode){
36982         ghostNode.appendChild(this.elNode.cloneNode(true));
36983     },
36984
36985     getDDRepairXY : function(){
36986         return Roo.lib.Dom.getXY(this.iconNode);
36987     },
36988
36989     onRender : function(){
36990         this.render();
36991     },
36992
36993     render : function(bulkRender){
36994         var n = this.node, a = n.attributes;
36995         var targetNode = n.parentNode ?
36996               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36997
36998         if(!this.rendered){
36999             this.rendered = true;
37000
37001             this.renderElements(n, a, targetNode, bulkRender);
37002
37003             if(a.qtip){
37004                if(this.textNode.setAttributeNS){
37005                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37006                    if(a.qtipTitle){
37007                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37008                    }
37009                }else{
37010                    this.textNode.setAttribute("ext:qtip", a.qtip);
37011                    if(a.qtipTitle){
37012                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37013                    }
37014                }
37015             }else if(a.qtipCfg){
37016                 a.qtipCfg.target = Roo.id(this.textNode);
37017                 Roo.QuickTips.register(a.qtipCfg);
37018             }
37019             this.initEvents();
37020             if(!this.node.expanded){
37021                 this.updateExpandIcon();
37022             }
37023         }else{
37024             if(bulkRender === true) {
37025                 targetNode.appendChild(this.wrap);
37026             }
37027         }
37028     },
37029
37030     renderElements : function(n, a, targetNode, bulkRender)
37031     {
37032         // add some indent caching, this helps performance when rendering a large tree
37033         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37034         var t = n.getOwnerTree();
37035         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37036         if (typeof(n.attributes.html) != 'undefined') {
37037             txt = n.attributes.html;
37038         }
37039         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37040         var cb = typeof a.checked == 'boolean';
37041         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37042         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37043             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37044             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37045             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37046             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37047             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37048              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37049                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37050             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37051             "</li>"];
37052
37053         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37054             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37055                                 n.nextSibling.ui.getEl(), buf.join(""));
37056         }else{
37057             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37058         }
37059
37060         this.elNode = this.wrap.childNodes[0];
37061         this.ctNode = this.wrap.childNodes[1];
37062         var cs = this.elNode.childNodes;
37063         this.indentNode = cs[0];
37064         this.ecNode = cs[1];
37065         this.iconNode = cs[2];
37066         var index = 3;
37067         if(cb){
37068             this.checkbox = cs[3];
37069             index++;
37070         }
37071         this.anchor = cs[index];
37072         this.textNode = cs[index].firstChild;
37073     },
37074
37075     getAnchor : function(){
37076         return this.anchor;
37077     },
37078
37079     getTextEl : function(){
37080         return this.textNode;
37081     },
37082
37083     getIconEl : function(){
37084         return this.iconNode;
37085     },
37086
37087     isChecked : function(){
37088         return this.checkbox ? this.checkbox.checked : false;
37089     },
37090
37091     updateExpandIcon : function(){
37092         if(this.rendered){
37093             var n = this.node, c1, c2;
37094             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37095             var hasChild = n.hasChildNodes();
37096             if(hasChild){
37097                 if(n.expanded){
37098                     cls += "-minus";
37099                     c1 = "x-tree-node-collapsed";
37100                     c2 = "x-tree-node-expanded";
37101                 }else{
37102                     cls += "-plus";
37103                     c1 = "x-tree-node-expanded";
37104                     c2 = "x-tree-node-collapsed";
37105                 }
37106                 if(this.wasLeaf){
37107                     this.removeClass("x-tree-node-leaf");
37108                     this.wasLeaf = false;
37109                 }
37110                 if(this.c1 != c1 || this.c2 != c2){
37111                     Roo.fly(this.elNode).replaceClass(c1, c2);
37112                     this.c1 = c1; this.c2 = c2;
37113                 }
37114             }else{
37115                 // this changes non-leafs into leafs if they have no children.
37116                 // it's not very rational behaviour..
37117                 
37118                 if(!this.wasLeaf && this.node.leaf){
37119                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37120                     delete this.c1;
37121                     delete this.c2;
37122                     this.wasLeaf = true;
37123                 }
37124             }
37125             var ecc = "x-tree-ec-icon "+cls;
37126             if(this.ecc != ecc){
37127                 this.ecNode.className = ecc;
37128                 this.ecc = ecc;
37129             }
37130         }
37131     },
37132
37133     getChildIndent : function(){
37134         if(!this.childIndent){
37135             var buf = [];
37136             var p = this.node;
37137             while(p){
37138                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37139                     if(!p.isLast()) {
37140                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37141                     } else {
37142                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37143                     }
37144                 }
37145                 p = p.parentNode;
37146             }
37147             this.childIndent = buf.join("");
37148         }
37149         return this.childIndent;
37150     },
37151
37152     renderIndent : function(){
37153         if(this.rendered){
37154             var indent = "";
37155             var p = this.node.parentNode;
37156             if(p){
37157                 indent = p.ui.getChildIndent();
37158             }
37159             if(this.indentMarkup != indent){ // don't rerender if not required
37160                 this.indentNode.innerHTML = indent;
37161                 this.indentMarkup = indent;
37162             }
37163             this.updateExpandIcon();
37164         }
37165     }
37166 };
37167
37168 Roo.tree.RootTreeNodeUI = function(){
37169     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37170 };
37171 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37172     render : function(){
37173         if(!this.rendered){
37174             var targetNode = this.node.ownerTree.innerCt.dom;
37175             this.node.expanded = true;
37176             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37177             this.wrap = this.ctNode = targetNode.firstChild;
37178         }
37179     },
37180     collapse : function(){
37181     },
37182     expand : function(){
37183     }
37184 });/*
37185  * Based on:
37186  * Ext JS Library 1.1.1
37187  * Copyright(c) 2006-2007, Ext JS, LLC.
37188  *
37189  * Originally Released Under LGPL - original licence link has changed is not relivant.
37190  *
37191  * Fork - LGPL
37192  * <script type="text/javascript">
37193  */
37194 /**
37195  * @class Roo.tree.TreeLoader
37196  * @extends Roo.util.Observable
37197  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37198  * nodes from a specified URL. The response must be a javascript Array definition
37199  * who's elements are node definition objects. eg:
37200  * <pre><code>
37201 {  success : true,
37202    data :      [
37203    
37204     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37205     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37206     ]
37207 }
37208
37209
37210 </code></pre>
37211  * <br><br>
37212  * The old style respose with just an array is still supported, but not recommended.
37213  * <br><br>
37214  *
37215  * A server request is sent, and child nodes are loaded only when a node is expanded.
37216  * The loading node's id is passed to the server under the parameter name "node" to
37217  * enable the server to produce the correct child nodes.
37218  * <br><br>
37219  * To pass extra parameters, an event handler may be attached to the "beforeload"
37220  * event, and the parameters specified in the TreeLoader's baseParams property:
37221  * <pre><code>
37222     myTreeLoader.on("beforeload", function(treeLoader, node) {
37223         this.baseParams.category = node.attributes.category;
37224     }, this);
37225     
37226 </code></pre>
37227  *
37228  * This would pass an HTTP parameter called "category" to the server containing
37229  * the value of the Node's "category" attribute.
37230  * @constructor
37231  * Creates a new Treeloader.
37232  * @param {Object} config A config object containing config properties.
37233  */
37234 Roo.tree.TreeLoader = function(config){
37235     this.baseParams = {};
37236     this.requestMethod = "POST";
37237     Roo.apply(this, config);
37238
37239     this.addEvents({
37240     
37241         /**
37242          * @event beforeload
37243          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37244          * @param {Object} This TreeLoader object.
37245          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37246          * @param {Object} callback The callback function specified in the {@link #load} call.
37247          */
37248         beforeload : true,
37249         /**
37250          * @event load
37251          * Fires when the node has been successfuly loaded.
37252          * @param {Object} This TreeLoader object.
37253          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37254          * @param {Object} response The response object containing the data from the server.
37255          */
37256         load : true,
37257         /**
37258          * @event loadexception
37259          * Fires if the network request failed.
37260          * @param {Object} This TreeLoader object.
37261          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37262          * @param {Object} response The response object containing the data from the server.
37263          */
37264         loadexception : true,
37265         /**
37266          * @event create
37267          * Fires before a node is created, enabling you to return custom Node types 
37268          * @param {Object} This TreeLoader object.
37269          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37270          */
37271         create : true
37272     });
37273
37274     Roo.tree.TreeLoader.superclass.constructor.call(this);
37275 };
37276
37277 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37278     /**
37279     * @cfg {String} dataUrl The URL from which to request a Json string which
37280     * specifies an array of node definition object representing the child nodes
37281     * to be loaded.
37282     */
37283     /**
37284     * @cfg {String} requestMethod either GET or POST
37285     * defaults to POST (due to BC)
37286     * to be loaded.
37287     */
37288     /**
37289     * @cfg {Object} baseParams (optional) An object containing properties which
37290     * specify HTTP parameters to be passed to each request for child nodes.
37291     */
37292     /**
37293     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37294     * created by this loader. If the attributes sent by the server have an attribute in this object,
37295     * they take priority.
37296     */
37297     /**
37298     * @cfg {Object} uiProviders (optional) An object containing properties which
37299     * 
37300     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37301     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37302     * <i>uiProvider</i> attribute of a returned child node is a string rather
37303     * than a reference to a TreeNodeUI implementation, this that string value
37304     * is used as a property name in the uiProviders object. You can define the provider named
37305     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37306     */
37307     uiProviders : {},
37308
37309     /**
37310     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37311     * child nodes before loading.
37312     */
37313     clearOnLoad : true,
37314
37315     /**
37316     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37317     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37318     * Grid query { data : [ .....] }
37319     */
37320     
37321     root : false,
37322      /**
37323     * @cfg {String} queryParam (optional) 
37324     * Name of the query as it will be passed on the querystring (defaults to 'node')
37325     * eg. the request will be ?node=[id]
37326     */
37327     
37328     
37329     queryParam: false,
37330     
37331     /**
37332      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37333      * This is called automatically when a node is expanded, but may be used to reload
37334      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37335      * @param {Roo.tree.TreeNode} node
37336      * @param {Function} callback
37337      */
37338     load : function(node, callback){
37339         if(this.clearOnLoad){
37340             while(node.firstChild){
37341                 node.removeChild(node.firstChild);
37342             }
37343         }
37344         if(node.attributes.children){ // preloaded json children
37345             var cs = node.attributes.children;
37346             for(var i = 0, len = cs.length; i < len; i++){
37347                 node.appendChild(this.createNode(cs[i]));
37348             }
37349             if(typeof callback == "function"){
37350                 callback();
37351             }
37352         }else if(this.dataUrl){
37353             this.requestData(node, callback);
37354         }
37355     },
37356
37357     getParams: function(node){
37358         var buf = [], bp = this.baseParams;
37359         for(var key in bp){
37360             if(typeof bp[key] != "function"){
37361                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37362             }
37363         }
37364         var n = this.queryParam === false ? 'node' : this.queryParam;
37365         buf.push(n + "=", encodeURIComponent(node.id));
37366         return buf.join("");
37367     },
37368
37369     requestData : function(node, callback){
37370         if(this.fireEvent("beforeload", this, node, callback) !== false){
37371             this.transId = Roo.Ajax.request({
37372                 method:this.requestMethod,
37373                 url: this.dataUrl||this.url,
37374                 success: this.handleResponse,
37375                 failure: this.handleFailure,
37376                 scope: this,
37377                 argument: {callback: callback, node: node},
37378                 params: this.getParams(node)
37379             });
37380         }else{
37381             // if the load is cancelled, make sure we notify
37382             // the node that we are done
37383             if(typeof callback == "function"){
37384                 callback();
37385             }
37386         }
37387     },
37388
37389     isLoading : function(){
37390         return this.transId ? true : false;
37391     },
37392
37393     abort : function(){
37394         if(this.isLoading()){
37395             Roo.Ajax.abort(this.transId);
37396         }
37397     },
37398
37399     // private
37400     createNode : function(attr)
37401     {
37402         // apply baseAttrs, nice idea Corey!
37403         if(this.baseAttrs){
37404             Roo.applyIf(attr, this.baseAttrs);
37405         }
37406         if(this.applyLoader !== false){
37407             attr.loader = this;
37408         }
37409         // uiProvider = depreciated..
37410         
37411         if(typeof(attr.uiProvider) == 'string'){
37412            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37413                 /**  eval:var:attr */ eval(attr.uiProvider);
37414         }
37415         if(typeof(this.uiProviders['default']) != 'undefined') {
37416             attr.uiProvider = this.uiProviders['default'];
37417         }
37418         
37419         this.fireEvent('create', this, attr);
37420         
37421         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37422         return(attr.leaf ?
37423                         new Roo.tree.TreeNode(attr) :
37424                         new Roo.tree.AsyncTreeNode(attr));
37425     },
37426
37427     processResponse : function(response, node, callback)
37428     {
37429         var json = response.responseText;
37430         try {
37431             
37432             var o = Roo.decode(json);
37433             
37434             if (this.root === false && typeof(o.success) != undefined) {
37435                 this.root = 'data'; // the default behaviour for list like data..
37436                 }
37437                 
37438             if (this.root !== false &&  !o.success) {
37439                 // it's a failure condition.
37440                 var a = response.argument;
37441                 this.fireEvent("loadexception", this, a.node, response);
37442                 Roo.log("Load failed - should have a handler really");
37443                 return;
37444             }
37445             
37446             
37447             
37448             if (this.root !== false) {
37449                  o = o[this.root];
37450             }
37451             
37452             for(var i = 0, len = o.length; i < len; i++){
37453                 var n = this.createNode(o[i]);
37454                 if(n){
37455                     node.appendChild(n);
37456                 }
37457             }
37458             if(typeof callback == "function"){
37459                 callback(this, node);
37460             }
37461         }catch(e){
37462             this.handleFailure(response);
37463         }
37464     },
37465
37466     handleResponse : function(response){
37467         this.transId = false;
37468         var a = response.argument;
37469         this.processResponse(response, a.node, a.callback);
37470         this.fireEvent("load", this, a.node, response);
37471     },
37472
37473     handleFailure : function(response)
37474     {
37475         // should handle failure better..
37476         this.transId = false;
37477         var a = response.argument;
37478         this.fireEvent("loadexception", this, a.node, response);
37479         if(typeof a.callback == "function"){
37480             a.callback(this, a.node);
37481         }
37482     }
37483 });/*
37484  * Based on:
37485  * Ext JS Library 1.1.1
37486  * Copyright(c) 2006-2007, Ext JS, LLC.
37487  *
37488  * Originally Released Under LGPL - original licence link has changed is not relivant.
37489  *
37490  * Fork - LGPL
37491  * <script type="text/javascript">
37492  */
37493
37494 /**
37495 * @class Roo.tree.TreeFilter
37496 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37497 * @param {TreePanel} tree
37498 * @param {Object} config (optional)
37499  */
37500 Roo.tree.TreeFilter = function(tree, config){
37501     this.tree = tree;
37502     this.filtered = {};
37503     Roo.apply(this, config);
37504 };
37505
37506 Roo.tree.TreeFilter.prototype = {
37507     clearBlank:false,
37508     reverse:false,
37509     autoClear:false,
37510     remove:false,
37511
37512      /**
37513      * Filter the data by a specific attribute.
37514      * @param {String/RegExp} value Either string that the attribute value
37515      * should start with or a RegExp to test against the attribute
37516      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37517      * @param {TreeNode} startNode (optional) The node to start the filter at.
37518      */
37519     filter : function(value, attr, startNode){
37520         attr = attr || "text";
37521         var f;
37522         if(typeof value == "string"){
37523             var vlen = value.length;
37524             // auto clear empty filter
37525             if(vlen == 0 && this.clearBlank){
37526                 this.clear();
37527                 return;
37528             }
37529             value = value.toLowerCase();
37530             f = function(n){
37531                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37532             };
37533         }else if(value.exec){ // regex?
37534             f = function(n){
37535                 return value.test(n.attributes[attr]);
37536             };
37537         }else{
37538             throw 'Illegal filter type, must be string or regex';
37539         }
37540         this.filterBy(f, null, startNode);
37541         },
37542
37543     /**
37544      * Filter by a function. The passed function will be called with each
37545      * node in the tree (or from the startNode). If the function returns true, the node is kept
37546      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37547      * @param {Function} fn The filter function
37548      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37549      */
37550     filterBy : function(fn, scope, startNode){
37551         startNode = startNode || this.tree.root;
37552         if(this.autoClear){
37553             this.clear();
37554         }
37555         var af = this.filtered, rv = this.reverse;
37556         var f = function(n){
37557             if(n == startNode){
37558                 return true;
37559             }
37560             if(af[n.id]){
37561                 return false;
37562             }
37563             var m = fn.call(scope || n, n);
37564             if(!m || rv){
37565                 af[n.id] = n;
37566                 n.ui.hide();
37567                 return false;
37568             }
37569             return true;
37570         };
37571         startNode.cascade(f);
37572         if(this.remove){
37573            for(var id in af){
37574                if(typeof id != "function"){
37575                    var n = af[id];
37576                    if(n && n.parentNode){
37577                        n.parentNode.removeChild(n);
37578                    }
37579                }
37580            }
37581         }
37582     },
37583
37584     /**
37585      * Clears the current filter. Note: with the "remove" option
37586      * set a filter cannot be cleared.
37587      */
37588     clear : function(){
37589         var t = this.tree;
37590         var af = this.filtered;
37591         for(var id in af){
37592             if(typeof id != "function"){
37593                 var n = af[id];
37594                 if(n){
37595                     n.ui.show();
37596                 }
37597             }
37598         }
37599         this.filtered = {};
37600     }
37601 };
37602 /*
37603  * Based on:
37604  * Ext JS Library 1.1.1
37605  * Copyright(c) 2006-2007, Ext JS, LLC.
37606  *
37607  * Originally Released Under LGPL - original licence link has changed is not relivant.
37608  *
37609  * Fork - LGPL
37610  * <script type="text/javascript">
37611  */
37612  
37613
37614 /**
37615  * @class Roo.tree.TreeSorter
37616  * Provides sorting of nodes in a TreePanel
37617  * 
37618  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37619  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37620  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37621  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37622  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37623  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37624  * @constructor
37625  * @param {TreePanel} tree
37626  * @param {Object} config
37627  */
37628 Roo.tree.TreeSorter = function(tree, config){
37629     Roo.apply(this, config);
37630     tree.on("beforechildrenrendered", this.doSort, this);
37631     tree.on("append", this.updateSort, this);
37632     tree.on("insert", this.updateSort, this);
37633     
37634     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37635     var p = this.property || "text";
37636     var sortType = this.sortType;
37637     var fs = this.folderSort;
37638     var cs = this.caseSensitive === true;
37639     var leafAttr = this.leafAttr || 'leaf';
37640
37641     this.sortFn = function(n1, n2){
37642         if(fs){
37643             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37644                 return 1;
37645             }
37646             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37647                 return -1;
37648             }
37649         }
37650         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37651         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37652         if(v1 < v2){
37653                         return dsc ? +1 : -1;
37654                 }else if(v1 > v2){
37655                         return dsc ? -1 : +1;
37656         }else{
37657                 return 0;
37658         }
37659     };
37660 };
37661
37662 Roo.tree.TreeSorter.prototype = {
37663     doSort : function(node){
37664         node.sort(this.sortFn);
37665     },
37666     
37667     compareNodes : function(n1, n2){
37668         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37669     },
37670     
37671     updateSort : function(tree, node){
37672         if(node.childrenRendered){
37673             this.doSort.defer(1, this, [node]);
37674         }
37675     }
37676 };/*
37677  * Based on:
37678  * Ext JS Library 1.1.1
37679  * Copyright(c) 2006-2007, Ext JS, LLC.
37680  *
37681  * Originally Released Under LGPL - original licence link has changed is not relivant.
37682  *
37683  * Fork - LGPL
37684  * <script type="text/javascript">
37685  */
37686
37687 if(Roo.dd.DropZone){
37688     
37689 Roo.tree.TreeDropZone = function(tree, config){
37690     this.allowParentInsert = false;
37691     this.allowContainerDrop = false;
37692     this.appendOnly = false;
37693     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37694     this.tree = tree;
37695     this.lastInsertClass = "x-tree-no-status";
37696     this.dragOverData = {};
37697 };
37698
37699 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37700     ddGroup : "TreeDD",
37701     scroll:  true,
37702     
37703     expandDelay : 1000,
37704     
37705     expandNode : function(node){
37706         if(node.hasChildNodes() && !node.isExpanded()){
37707             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37708         }
37709     },
37710     
37711     queueExpand : function(node){
37712         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37713     },
37714     
37715     cancelExpand : function(){
37716         if(this.expandProcId){
37717             clearTimeout(this.expandProcId);
37718             this.expandProcId = false;
37719         }
37720     },
37721     
37722     isValidDropPoint : function(n, pt, dd, e, data){
37723         if(!n || !data){ return false; }
37724         var targetNode = n.node;
37725         var dropNode = data.node;
37726         // default drop rules
37727         if(!(targetNode && targetNode.isTarget && pt)){
37728             return false;
37729         }
37730         if(pt == "append" && targetNode.allowChildren === false){
37731             return false;
37732         }
37733         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37734             return false;
37735         }
37736         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37737             return false;
37738         }
37739         // reuse the object
37740         var overEvent = this.dragOverData;
37741         overEvent.tree = this.tree;
37742         overEvent.target = targetNode;
37743         overEvent.data = data;
37744         overEvent.point = pt;
37745         overEvent.source = dd;
37746         overEvent.rawEvent = e;
37747         overEvent.dropNode = dropNode;
37748         overEvent.cancel = false;  
37749         var result = this.tree.fireEvent("nodedragover", overEvent);
37750         return overEvent.cancel === false && result !== false;
37751     },
37752     
37753     getDropPoint : function(e, n, dd)
37754     {
37755         var tn = n.node;
37756         if(tn.isRoot){
37757             return tn.allowChildren !== false ? "append" : false; // always append for root
37758         }
37759         var dragEl = n.ddel;
37760         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37761         var y = Roo.lib.Event.getPageY(e);
37762         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37763         
37764         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37765         var noAppend = tn.allowChildren === false;
37766         if(this.appendOnly || tn.parentNode.allowChildren === false){
37767             return noAppend ? false : "append";
37768         }
37769         var noBelow = false;
37770         if(!this.allowParentInsert){
37771             noBelow = tn.hasChildNodes() && tn.isExpanded();
37772         }
37773         var q = (b - t) / (noAppend ? 2 : 3);
37774         if(y >= t && y < (t + q)){
37775             return "above";
37776         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37777             return "below";
37778         }else{
37779             return "append";
37780         }
37781     },
37782     
37783     onNodeEnter : function(n, dd, e, data)
37784     {
37785         this.cancelExpand();
37786     },
37787     
37788     onNodeOver : function(n, dd, e, data)
37789     {
37790        
37791         var pt = this.getDropPoint(e, n, dd);
37792         var node = n.node;
37793         
37794         // auto node expand check
37795         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37796             this.queueExpand(node);
37797         }else if(pt != "append"){
37798             this.cancelExpand();
37799         }
37800         
37801         // set the insert point style on the target node
37802         var returnCls = this.dropNotAllowed;
37803         if(this.isValidDropPoint(n, pt, dd, e, data)){
37804            if(pt){
37805                var el = n.ddel;
37806                var cls;
37807                if(pt == "above"){
37808                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37809                    cls = "x-tree-drag-insert-above";
37810                }else if(pt == "below"){
37811                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37812                    cls = "x-tree-drag-insert-below";
37813                }else{
37814                    returnCls = "x-tree-drop-ok-append";
37815                    cls = "x-tree-drag-append";
37816                }
37817                if(this.lastInsertClass != cls){
37818                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37819                    this.lastInsertClass = cls;
37820                }
37821            }
37822        }
37823        return returnCls;
37824     },
37825     
37826     onNodeOut : function(n, dd, e, data){
37827         
37828         this.cancelExpand();
37829         this.removeDropIndicators(n);
37830     },
37831     
37832     onNodeDrop : function(n, dd, e, data){
37833         var point = this.getDropPoint(e, n, dd);
37834         var targetNode = n.node;
37835         targetNode.ui.startDrop();
37836         if(!this.isValidDropPoint(n, point, dd, e, data)){
37837             targetNode.ui.endDrop();
37838             return false;
37839         }
37840         // first try to find the drop node
37841         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37842         var dropEvent = {
37843             tree : this.tree,
37844             target: targetNode,
37845             data: data,
37846             point: point,
37847             source: dd,
37848             rawEvent: e,
37849             dropNode: dropNode,
37850             cancel: !dropNode   
37851         };
37852         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37853         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37854             targetNode.ui.endDrop();
37855             return false;
37856         }
37857         // allow target changing
37858         targetNode = dropEvent.target;
37859         if(point == "append" && !targetNode.isExpanded()){
37860             targetNode.expand(false, null, function(){
37861                 this.completeDrop(dropEvent);
37862             }.createDelegate(this));
37863         }else{
37864             this.completeDrop(dropEvent);
37865         }
37866         return true;
37867     },
37868     
37869     completeDrop : function(de){
37870         var ns = de.dropNode, p = de.point, t = de.target;
37871         if(!(ns instanceof Array)){
37872             ns = [ns];
37873         }
37874         var n;
37875         for(var i = 0, len = ns.length; i < len; i++){
37876             n = ns[i];
37877             if(p == "above"){
37878                 t.parentNode.insertBefore(n, t);
37879             }else if(p == "below"){
37880                 t.parentNode.insertBefore(n, t.nextSibling);
37881             }else{
37882                 t.appendChild(n);
37883             }
37884         }
37885         n.ui.focus();
37886         if(this.tree.hlDrop){
37887             n.ui.highlight();
37888         }
37889         t.ui.endDrop();
37890         this.tree.fireEvent("nodedrop", de);
37891     },
37892     
37893     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37894         if(this.tree.hlDrop){
37895             dropNode.ui.focus();
37896             dropNode.ui.highlight();
37897         }
37898         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37899     },
37900     
37901     getTree : function(){
37902         return this.tree;
37903     },
37904     
37905     removeDropIndicators : function(n){
37906         if(n && n.ddel){
37907             var el = n.ddel;
37908             Roo.fly(el).removeClass([
37909                     "x-tree-drag-insert-above",
37910                     "x-tree-drag-insert-below",
37911                     "x-tree-drag-append"]);
37912             this.lastInsertClass = "_noclass";
37913         }
37914     },
37915     
37916     beforeDragDrop : function(target, e, id){
37917         this.cancelExpand();
37918         return true;
37919     },
37920     
37921     afterRepair : function(data){
37922         if(data && Roo.enableFx){
37923             data.node.ui.highlight();
37924         }
37925         this.hideProxy();
37926     } 
37927     
37928 });
37929
37930 }
37931 /*
37932  * Based on:
37933  * Ext JS Library 1.1.1
37934  * Copyright(c) 2006-2007, Ext JS, LLC.
37935  *
37936  * Originally Released Under LGPL - original licence link has changed is not relivant.
37937  *
37938  * Fork - LGPL
37939  * <script type="text/javascript">
37940  */
37941  
37942
37943 if(Roo.dd.DragZone){
37944 Roo.tree.TreeDragZone = function(tree, config){
37945     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37946     this.tree = tree;
37947 };
37948
37949 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37950     ddGroup : "TreeDD",
37951    
37952     onBeforeDrag : function(data, e){
37953         var n = data.node;
37954         return n && n.draggable && !n.disabled;
37955     },
37956      
37957     
37958     onInitDrag : function(e){
37959         var data = this.dragData;
37960         this.tree.getSelectionModel().select(data.node);
37961         this.proxy.update("");
37962         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37963         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37964     },
37965     
37966     getRepairXY : function(e, data){
37967         return data.node.ui.getDDRepairXY();
37968     },
37969     
37970     onEndDrag : function(data, e){
37971         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37972         
37973         
37974     },
37975     
37976     onValidDrop : function(dd, e, id){
37977         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37978         this.hideProxy();
37979     },
37980     
37981     beforeInvalidDrop : function(e, id){
37982         // this scrolls the original position back into view
37983         var sm = this.tree.getSelectionModel();
37984         sm.clearSelections();
37985         sm.select(this.dragData.node);
37986     }
37987 });
37988 }/*
37989  * Based on:
37990  * Ext JS Library 1.1.1
37991  * Copyright(c) 2006-2007, Ext JS, LLC.
37992  *
37993  * Originally Released Under LGPL - original licence link has changed is not relivant.
37994  *
37995  * Fork - LGPL
37996  * <script type="text/javascript">
37997  */
37998 /**
37999  * @class Roo.tree.TreeEditor
38000  * @extends Roo.Editor
38001  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38002  * as the editor field.
38003  * @constructor
38004  * @param {Object} config (used to be the tree panel.)
38005  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38006  * 
38007  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38008  * @cfg {Roo.form.TextField} field [required] The field configuration
38009  *
38010  * 
38011  */
38012 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38013     var tree = config;
38014     var field;
38015     if (oldconfig) { // old style..
38016         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38017     } else {
38018         // new style..
38019         tree = config.tree;
38020         config.field = config.field  || {};
38021         config.field.xtype = 'TextField';
38022         field = Roo.factory(config.field, Roo.form);
38023     }
38024     config = config || {};
38025     
38026     
38027     this.addEvents({
38028         /**
38029          * @event beforenodeedit
38030          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38031          * false from the handler of this event.
38032          * @param {Editor} this
38033          * @param {Roo.tree.Node} node 
38034          */
38035         "beforenodeedit" : true
38036     });
38037     
38038     //Roo.log(config);
38039     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38040
38041     this.tree = tree;
38042
38043     tree.on('beforeclick', this.beforeNodeClick, this);
38044     tree.getTreeEl().on('mousedown', this.hide, this);
38045     this.on('complete', this.updateNode, this);
38046     this.on('beforestartedit', this.fitToTree, this);
38047     this.on('startedit', this.bindScroll, this, {delay:10});
38048     this.on('specialkey', this.onSpecialKey, this);
38049 };
38050
38051 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38052     /**
38053      * @cfg {String} alignment
38054      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38055      */
38056     alignment: "l-l",
38057     // inherit
38058     autoSize: false,
38059     /**
38060      * @cfg {Boolean} hideEl
38061      * True to hide the bound element while the editor is displayed (defaults to false)
38062      */
38063     hideEl : false,
38064     /**
38065      * @cfg {String} cls
38066      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38067      */
38068     cls: "x-small-editor x-tree-editor",
38069     /**
38070      * @cfg {Boolean} shim
38071      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38072      */
38073     shim:false,
38074     // inherit
38075     shadow:"frame",
38076     /**
38077      * @cfg {Number} maxWidth
38078      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38079      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38080      * scroll and client offsets into account prior to each edit.
38081      */
38082     maxWidth: 250,
38083
38084     editDelay : 350,
38085
38086     // private
38087     fitToTree : function(ed, el){
38088         var td = this.tree.getTreeEl().dom, nd = el.dom;
38089         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38090             td.scrollLeft = nd.offsetLeft;
38091         }
38092         var w = Math.min(
38093                 this.maxWidth,
38094                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38095         this.setSize(w, '');
38096         
38097         return this.fireEvent('beforenodeedit', this, this.editNode);
38098         
38099     },
38100
38101     // private
38102     triggerEdit : function(node){
38103         this.completeEdit();
38104         this.editNode = node;
38105         this.startEdit(node.ui.textNode, node.text);
38106     },
38107
38108     // private
38109     bindScroll : function(){
38110         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38111     },
38112
38113     // private
38114     beforeNodeClick : function(node, e){
38115         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38116         this.lastClick = new Date();
38117         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38118             e.stopEvent();
38119             this.triggerEdit(node);
38120             return false;
38121         }
38122         return true;
38123     },
38124
38125     // private
38126     updateNode : function(ed, value){
38127         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38128         this.editNode.setText(value);
38129     },
38130
38131     // private
38132     onHide : function(){
38133         Roo.tree.TreeEditor.superclass.onHide.call(this);
38134         if(this.editNode){
38135             this.editNode.ui.focus();
38136         }
38137     },
38138
38139     // private
38140     onSpecialKey : function(field, e){
38141         var k = e.getKey();
38142         if(k == e.ESC){
38143             e.stopEvent();
38144             this.cancelEdit();
38145         }else if(k == e.ENTER && !e.hasModifier()){
38146             e.stopEvent();
38147             this.completeEdit();
38148         }
38149     }
38150 });//<Script type="text/javascript">
38151 /*
38152  * Based on:
38153  * Ext JS Library 1.1.1
38154  * Copyright(c) 2006-2007, Ext JS, LLC.
38155  *
38156  * Originally Released Under LGPL - original licence link has changed is not relivant.
38157  *
38158  * Fork - LGPL
38159  * <script type="text/javascript">
38160  */
38161  
38162 /**
38163  * Not documented??? - probably should be...
38164  */
38165
38166 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38167     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38168     
38169     renderElements : function(n, a, targetNode, bulkRender){
38170         //consel.log("renderElements?");
38171         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38172
38173         var t = n.getOwnerTree();
38174         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38175         
38176         var cols = t.columns;
38177         var bw = t.borderWidth;
38178         var c = cols[0];
38179         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38180          var cb = typeof a.checked == "boolean";
38181         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38182         var colcls = 'x-t-' + tid + '-c0';
38183         var buf = [
38184             '<li class="x-tree-node">',
38185             
38186                 
38187                 '<div class="x-tree-node-el ', a.cls,'">',
38188                     // extran...
38189                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38190                 
38191                 
38192                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38193                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38194                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38195                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38196                            (a.iconCls ? ' '+a.iconCls : ''),
38197                            '" unselectable="on" />',
38198                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38199                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38200                              
38201                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38202                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38203                             '<span unselectable="on" qtip="' + tx + '">',
38204                              tx,
38205                              '</span></a>' ,
38206                     '</div>',
38207                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38208                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38209                  ];
38210         for(var i = 1, len = cols.length; i < len; i++){
38211             c = cols[i];
38212             colcls = 'x-t-' + tid + '-c' +i;
38213             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38214             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38215                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38216                       "</div>");
38217          }
38218          
38219          buf.push(
38220             '</a>',
38221             '<div class="x-clear"></div></div>',
38222             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38223             "</li>");
38224         
38225         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38226             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38227                                 n.nextSibling.ui.getEl(), buf.join(""));
38228         }else{
38229             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38230         }
38231         var el = this.wrap.firstChild;
38232         this.elRow = el;
38233         this.elNode = el.firstChild;
38234         this.ranchor = el.childNodes[1];
38235         this.ctNode = this.wrap.childNodes[1];
38236         var cs = el.firstChild.childNodes;
38237         this.indentNode = cs[0];
38238         this.ecNode = cs[1];
38239         this.iconNode = cs[2];
38240         var index = 3;
38241         if(cb){
38242             this.checkbox = cs[3];
38243             index++;
38244         }
38245         this.anchor = cs[index];
38246         
38247         this.textNode = cs[index].firstChild;
38248         
38249         //el.on("click", this.onClick, this);
38250         //el.on("dblclick", this.onDblClick, this);
38251         
38252         
38253        // console.log(this);
38254     },
38255     initEvents : function(){
38256         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38257         
38258             
38259         var a = this.ranchor;
38260
38261         var el = Roo.get(a);
38262
38263         if(Roo.isOpera){ // opera render bug ignores the CSS
38264             el.setStyle("text-decoration", "none");
38265         }
38266
38267         el.on("click", this.onClick, this);
38268         el.on("dblclick", this.onDblClick, this);
38269         el.on("contextmenu", this.onContextMenu, this);
38270         
38271     },
38272     
38273     /*onSelectedChange : function(state){
38274         if(state){
38275             this.focus();
38276             this.addClass("x-tree-selected");
38277         }else{
38278             //this.blur();
38279             this.removeClass("x-tree-selected");
38280         }
38281     },*/
38282     addClass : function(cls){
38283         if(this.elRow){
38284             Roo.fly(this.elRow).addClass(cls);
38285         }
38286         
38287     },
38288     
38289     
38290     removeClass : function(cls){
38291         if(this.elRow){
38292             Roo.fly(this.elRow).removeClass(cls);
38293         }
38294     }
38295
38296     
38297     
38298 });//<Script type="text/javascript">
38299
38300 /*
38301  * Based on:
38302  * Ext JS Library 1.1.1
38303  * Copyright(c) 2006-2007, Ext JS, LLC.
38304  *
38305  * Originally Released Under LGPL - original licence link has changed is not relivant.
38306  *
38307  * Fork - LGPL
38308  * <script type="text/javascript">
38309  */
38310  
38311
38312 /**
38313  * @class Roo.tree.ColumnTree
38314  * @extends Roo.tree.TreePanel
38315  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38316  * @cfg {int} borderWidth  compined right/left border allowance
38317  * @constructor
38318  * @param {String/HTMLElement/Element} el The container element
38319  * @param {Object} config
38320  */
38321 Roo.tree.ColumnTree =  function(el, config)
38322 {
38323    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38324    this.addEvents({
38325         /**
38326         * @event resize
38327         * Fire this event on a container when it resizes
38328         * @param {int} w Width
38329         * @param {int} h Height
38330         */
38331        "resize" : true
38332     });
38333     this.on('resize', this.onResize, this);
38334 };
38335
38336 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38337     //lines:false,
38338     
38339     
38340     borderWidth: Roo.isBorderBox ? 0 : 2, 
38341     headEls : false,
38342     
38343     render : function(){
38344         // add the header.....
38345        
38346         Roo.tree.ColumnTree.superclass.render.apply(this);
38347         
38348         this.el.addClass('x-column-tree');
38349         
38350         this.headers = this.el.createChild(
38351             {cls:'x-tree-headers'},this.innerCt.dom);
38352    
38353         var cols = this.columns, c;
38354         var totalWidth = 0;
38355         this.headEls = [];
38356         var  len = cols.length;
38357         for(var i = 0; i < len; i++){
38358              c = cols[i];
38359              totalWidth += c.width;
38360             this.headEls.push(this.headers.createChild({
38361                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38362                  cn: {
38363                      cls:'x-tree-hd-text',
38364                      html: c.header
38365                  },
38366                  style:'width:'+(c.width-this.borderWidth)+'px;'
38367              }));
38368         }
38369         this.headers.createChild({cls:'x-clear'});
38370         // prevent floats from wrapping when clipped
38371         this.headers.setWidth(totalWidth);
38372         //this.innerCt.setWidth(totalWidth);
38373         this.innerCt.setStyle({ overflow: 'auto' });
38374         this.onResize(this.width, this.height);
38375              
38376         
38377     },
38378     onResize : function(w,h)
38379     {
38380         this.height = h;
38381         this.width = w;
38382         // resize cols..
38383         this.innerCt.setWidth(this.width);
38384         this.innerCt.setHeight(this.height-20);
38385         
38386         // headers...
38387         var cols = this.columns, c;
38388         var totalWidth = 0;
38389         var expEl = false;
38390         var len = cols.length;
38391         for(var i = 0; i < len; i++){
38392             c = cols[i];
38393             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38394                 // it's the expander..
38395                 expEl  = this.headEls[i];
38396                 continue;
38397             }
38398             totalWidth += c.width;
38399             
38400         }
38401         if (expEl) {
38402             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38403         }
38404         this.headers.setWidth(w-20);
38405
38406         
38407         
38408         
38409     }
38410 });
38411 /*
38412  * Based on:
38413  * Ext JS Library 1.1.1
38414  * Copyright(c) 2006-2007, Ext JS, LLC.
38415  *
38416  * Originally Released Under LGPL - original licence link has changed is not relivant.
38417  *
38418  * Fork - LGPL
38419  * <script type="text/javascript">
38420  */
38421  
38422 /**
38423  * @class Roo.menu.Menu
38424  * @extends Roo.util.Observable
38425  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38426  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38427  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38428  * @constructor
38429  * Creates a new Menu
38430  * @param {Object} config Configuration options
38431  */
38432 Roo.menu.Menu = function(config){
38433     
38434     Roo.menu.Menu.superclass.constructor.call(this, config);
38435     
38436     this.id = this.id || Roo.id();
38437     this.addEvents({
38438         /**
38439          * @event beforeshow
38440          * Fires before this menu is displayed
38441          * @param {Roo.menu.Menu} this
38442          */
38443         beforeshow : true,
38444         /**
38445          * @event beforehide
38446          * Fires before this menu is hidden
38447          * @param {Roo.menu.Menu} this
38448          */
38449         beforehide : true,
38450         /**
38451          * @event show
38452          * Fires after this menu is displayed
38453          * @param {Roo.menu.Menu} this
38454          */
38455         show : true,
38456         /**
38457          * @event hide
38458          * Fires after this menu is hidden
38459          * @param {Roo.menu.Menu} this
38460          */
38461         hide : true,
38462         /**
38463          * @event click
38464          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38465          * @param {Roo.menu.Menu} this
38466          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38467          * @param {Roo.EventObject} e
38468          */
38469         click : true,
38470         /**
38471          * @event mouseover
38472          * Fires when the mouse is hovering over this menu
38473          * @param {Roo.menu.Menu} this
38474          * @param {Roo.EventObject} e
38475          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38476          */
38477         mouseover : true,
38478         /**
38479          * @event mouseout
38480          * Fires when the mouse exits this menu
38481          * @param {Roo.menu.Menu} this
38482          * @param {Roo.EventObject} e
38483          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38484          */
38485         mouseout : true,
38486         /**
38487          * @event itemclick
38488          * Fires when a menu item contained in this menu is clicked
38489          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38490          * @param {Roo.EventObject} e
38491          */
38492         itemclick: true
38493     });
38494     if (this.registerMenu) {
38495         Roo.menu.MenuMgr.register(this);
38496     }
38497     
38498     var mis = this.items;
38499     this.items = new Roo.util.MixedCollection();
38500     if(mis){
38501         this.add.apply(this, mis);
38502     }
38503 };
38504
38505 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38506     /**
38507      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38508      */
38509     minWidth : 120,
38510     /**
38511      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38512      * for bottom-right shadow (defaults to "sides")
38513      */
38514     shadow : "sides",
38515     /**
38516      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38517      * this menu (defaults to "tl-tr?")
38518      */
38519     subMenuAlign : "tl-tr?",
38520     /**
38521      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38522      * relative to its element of origin (defaults to "tl-bl?")
38523      */
38524     defaultAlign : "tl-bl?",
38525     /**
38526      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38527      */
38528     allowOtherMenus : false,
38529     /**
38530      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38531      */
38532     registerMenu : true,
38533
38534     hidden:true,
38535
38536     // private
38537     render : function(){
38538         if(this.el){
38539             return;
38540         }
38541         var el = this.el = new Roo.Layer({
38542             cls: "x-menu",
38543             shadow:this.shadow,
38544             constrain: false,
38545             parentEl: this.parentEl || document.body,
38546             zindex:15000
38547         });
38548
38549         this.keyNav = new Roo.menu.MenuNav(this);
38550
38551         if(this.plain){
38552             el.addClass("x-menu-plain");
38553         }
38554         if(this.cls){
38555             el.addClass(this.cls);
38556         }
38557         // generic focus element
38558         this.focusEl = el.createChild({
38559             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38560         });
38561         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38562         //disabling touch- as it's causing issues ..
38563         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38564         ul.on('click'   , this.onClick, this);
38565         
38566         
38567         ul.on("mouseover", this.onMouseOver, this);
38568         ul.on("mouseout", this.onMouseOut, this);
38569         this.items.each(function(item){
38570             if (item.hidden) {
38571                 return;
38572             }
38573             
38574             var li = document.createElement("li");
38575             li.className = "x-menu-list-item";
38576             ul.dom.appendChild(li);
38577             item.render(li, this);
38578         }, this);
38579         this.ul = ul;
38580         this.autoWidth();
38581     },
38582
38583     // private
38584     autoWidth : function(){
38585         var el = this.el, ul = this.ul;
38586         if(!el){
38587             return;
38588         }
38589         var w = this.width;
38590         if(w){
38591             el.setWidth(w);
38592         }else if(Roo.isIE){
38593             el.setWidth(this.minWidth);
38594             var t = el.dom.offsetWidth; // force recalc
38595             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38596         }
38597     },
38598
38599     // private
38600     delayAutoWidth : function(){
38601         if(this.rendered){
38602             if(!this.awTask){
38603                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38604             }
38605             this.awTask.delay(20);
38606         }
38607     },
38608
38609     // private
38610     findTargetItem : function(e){
38611         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38612         if(t && t.menuItemId){
38613             return this.items.get(t.menuItemId);
38614         }
38615     },
38616
38617     // private
38618     onClick : function(e){
38619         Roo.log("menu.onClick");
38620         var t = this.findTargetItem(e);
38621         if(!t){
38622             return;
38623         }
38624         Roo.log(e);
38625         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38626             if(t == this.activeItem && t.shouldDeactivate(e)){
38627                 this.activeItem.deactivate();
38628                 delete this.activeItem;
38629                 return;
38630             }
38631             if(t.canActivate){
38632                 this.setActiveItem(t, true);
38633             }
38634             return;
38635             
38636             
38637         }
38638         
38639         t.onClick(e);
38640         this.fireEvent("click", this, t, e);
38641     },
38642
38643     // private
38644     setActiveItem : function(item, autoExpand){
38645         if(item != this.activeItem){
38646             if(this.activeItem){
38647                 this.activeItem.deactivate();
38648             }
38649             this.activeItem = item;
38650             item.activate(autoExpand);
38651         }else if(autoExpand){
38652             item.expandMenu();
38653         }
38654     },
38655
38656     // private
38657     tryActivate : function(start, step){
38658         var items = this.items;
38659         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38660             var item = items.get(i);
38661             if(!item.disabled && item.canActivate){
38662                 this.setActiveItem(item, false);
38663                 return item;
38664             }
38665         }
38666         return false;
38667     },
38668
38669     // private
38670     onMouseOver : function(e){
38671         var t;
38672         if(t = this.findTargetItem(e)){
38673             if(t.canActivate && !t.disabled){
38674                 this.setActiveItem(t, true);
38675             }
38676         }
38677         this.fireEvent("mouseover", this, e, t);
38678     },
38679
38680     // private
38681     onMouseOut : function(e){
38682         var t;
38683         if(t = this.findTargetItem(e)){
38684             if(t == this.activeItem && t.shouldDeactivate(e)){
38685                 this.activeItem.deactivate();
38686                 delete this.activeItem;
38687             }
38688         }
38689         this.fireEvent("mouseout", this, e, t);
38690     },
38691
38692     /**
38693      * Read-only.  Returns true if the menu is currently displayed, else false.
38694      * @type Boolean
38695      */
38696     isVisible : function(){
38697         return this.el && !this.hidden;
38698     },
38699
38700     /**
38701      * Displays this menu relative to another element
38702      * @param {String/HTMLElement/Roo.Element} element The element to align to
38703      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38704      * the element (defaults to this.defaultAlign)
38705      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38706      */
38707     show : function(el, pos, parentMenu){
38708         this.parentMenu = parentMenu;
38709         if(!this.el){
38710             this.render();
38711         }
38712         this.fireEvent("beforeshow", this);
38713         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38714     },
38715
38716     /**
38717      * Displays this menu at a specific xy position
38718      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38719      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38720      */
38721     showAt : function(xy, parentMenu, /* private: */_e){
38722         this.parentMenu = parentMenu;
38723         if(!this.el){
38724             this.render();
38725         }
38726         if(_e !== false){
38727             this.fireEvent("beforeshow", this);
38728             xy = this.el.adjustForConstraints(xy);
38729         }
38730         this.el.setXY(xy);
38731         this.el.show();
38732         this.hidden = false;
38733         this.focus();
38734         this.fireEvent("show", this);
38735     },
38736
38737     focus : function(){
38738         if(!this.hidden){
38739             this.doFocus.defer(50, this);
38740         }
38741     },
38742
38743     doFocus : function(){
38744         if(!this.hidden){
38745             this.focusEl.focus();
38746         }
38747     },
38748
38749     /**
38750      * Hides this menu and optionally all parent menus
38751      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38752      */
38753     hide : function(deep){
38754         if(this.el && this.isVisible()){
38755             this.fireEvent("beforehide", this);
38756             if(this.activeItem){
38757                 this.activeItem.deactivate();
38758                 this.activeItem = null;
38759             }
38760             this.el.hide();
38761             this.hidden = true;
38762             this.fireEvent("hide", this);
38763         }
38764         if(deep === true && this.parentMenu){
38765             this.parentMenu.hide(true);
38766         }
38767     },
38768
38769     /**
38770      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38771      * Any of the following are valid:
38772      * <ul>
38773      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38774      * <li>An HTMLElement object which will be converted to a menu item</li>
38775      * <li>A menu item config object that will be created as a new menu item</li>
38776      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38777      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38778      * </ul>
38779      * Usage:
38780      * <pre><code>
38781 // Create the menu
38782 var menu = new Roo.menu.Menu();
38783
38784 // Create a menu item to add by reference
38785 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38786
38787 // Add a bunch of items at once using different methods.
38788 // Only the last item added will be returned.
38789 var item = menu.add(
38790     menuItem,                // add existing item by ref
38791     'Dynamic Item',          // new TextItem
38792     '-',                     // new separator
38793     { text: 'Config Item' }  // new item by config
38794 );
38795 </code></pre>
38796      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38797      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38798      */
38799     add : function(){
38800         var a = arguments, l = a.length, item;
38801         for(var i = 0; i < l; i++){
38802             var el = a[i];
38803             if ((typeof(el) == "object") && el.xtype && el.xns) {
38804                 el = Roo.factory(el, Roo.menu);
38805             }
38806             
38807             if(el.render){ // some kind of Item
38808                 item = this.addItem(el);
38809             }else if(typeof el == "string"){ // string
38810                 if(el == "separator" || el == "-"){
38811                     item = this.addSeparator();
38812                 }else{
38813                     item = this.addText(el);
38814                 }
38815             }else if(el.tagName || el.el){ // element
38816                 item = this.addElement(el);
38817             }else if(typeof el == "object"){ // must be menu item config?
38818                 item = this.addMenuItem(el);
38819             }
38820         }
38821         return item;
38822     },
38823
38824     /**
38825      * Returns this menu's underlying {@link Roo.Element} object
38826      * @return {Roo.Element} The element
38827      */
38828     getEl : function(){
38829         if(!this.el){
38830             this.render();
38831         }
38832         return this.el;
38833     },
38834
38835     /**
38836      * Adds a separator bar to the menu
38837      * @return {Roo.menu.Item} The menu item that was added
38838      */
38839     addSeparator : function(){
38840         return this.addItem(new Roo.menu.Separator());
38841     },
38842
38843     /**
38844      * Adds an {@link Roo.Element} object to the menu
38845      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38846      * @return {Roo.menu.Item} The menu item that was added
38847      */
38848     addElement : function(el){
38849         return this.addItem(new Roo.menu.BaseItem(el));
38850     },
38851
38852     /**
38853      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38854      * @param {Roo.menu.Item} item The menu item to add
38855      * @return {Roo.menu.Item} The menu item that was added
38856      */
38857     addItem : function(item){
38858         this.items.add(item);
38859         if(this.ul){
38860             var li = document.createElement("li");
38861             li.className = "x-menu-list-item";
38862             this.ul.dom.appendChild(li);
38863             item.render(li, this);
38864             this.delayAutoWidth();
38865         }
38866         return item;
38867     },
38868
38869     /**
38870      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38871      * @param {Object} config A MenuItem config object
38872      * @return {Roo.menu.Item} The menu item that was added
38873      */
38874     addMenuItem : function(config){
38875         if(!(config instanceof Roo.menu.Item)){
38876             if(typeof config.checked == "boolean"){ // must be check menu item config?
38877                 config = new Roo.menu.CheckItem(config);
38878             }else{
38879                 config = new Roo.menu.Item(config);
38880             }
38881         }
38882         return this.addItem(config);
38883     },
38884
38885     /**
38886      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38887      * @param {String} text The text to display in the menu item
38888      * @return {Roo.menu.Item} The menu item that was added
38889      */
38890     addText : function(text){
38891         return this.addItem(new Roo.menu.TextItem({ text : text }));
38892     },
38893
38894     /**
38895      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38896      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38897      * @param {Roo.menu.Item} item The menu item to add
38898      * @return {Roo.menu.Item} The menu item that was added
38899      */
38900     insert : function(index, item){
38901         this.items.insert(index, item);
38902         if(this.ul){
38903             var li = document.createElement("li");
38904             li.className = "x-menu-list-item";
38905             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38906             item.render(li, this);
38907             this.delayAutoWidth();
38908         }
38909         return item;
38910     },
38911
38912     /**
38913      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38914      * @param {Roo.menu.Item} item The menu item to remove
38915      */
38916     remove : function(item){
38917         this.items.removeKey(item.id);
38918         item.destroy();
38919     },
38920
38921     /**
38922      * Removes and destroys all items in the menu
38923      */
38924     removeAll : function(){
38925         var f;
38926         while(f = this.items.first()){
38927             this.remove(f);
38928         }
38929     }
38930 });
38931
38932 // MenuNav is a private utility class used internally by the Menu
38933 Roo.menu.MenuNav = function(menu){
38934     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38935     this.scope = this.menu = menu;
38936 };
38937
38938 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38939     doRelay : function(e, h){
38940         var k = e.getKey();
38941         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38942             this.menu.tryActivate(0, 1);
38943             return false;
38944         }
38945         return h.call(this.scope || this, e, this.menu);
38946     },
38947
38948     up : function(e, m){
38949         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38950             m.tryActivate(m.items.length-1, -1);
38951         }
38952     },
38953
38954     down : function(e, m){
38955         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38956             m.tryActivate(0, 1);
38957         }
38958     },
38959
38960     right : function(e, m){
38961         if(m.activeItem){
38962             m.activeItem.expandMenu(true);
38963         }
38964     },
38965
38966     left : function(e, m){
38967         m.hide();
38968         if(m.parentMenu && m.parentMenu.activeItem){
38969             m.parentMenu.activeItem.activate();
38970         }
38971     },
38972
38973     enter : function(e, m){
38974         if(m.activeItem){
38975             e.stopPropagation();
38976             m.activeItem.onClick(e);
38977             m.fireEvent("click", this, m.activeItem);
38978             return true;
38979         }
38980     }
38981 });/*
38982  * Based on:
38983  * Ext JS Library 1.1.1
38984  * Copyright(c) 2006-2007, Ext JS, LLC.
38985  *
38986  * Originally Released Under LGPL - original licence link has changed is not relivant.
38987  *
38988  * Fork - LGPL
38989  * <script type="text/javascript">
38990  */
38991  
38992 /**
38993  * @class Roo.menu.MenuMgr
38994  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38995  * @static
38996  */
38997 Roo.menu.MenuMgr = function(){
38998    var menus, active, groups = {}, attached = false, lastShow = new Date();
38999
39000    // private - called when first menu is created
39001    function init(){
39002        menus = {};
39003        active = new Roo.util.MixedCollection();
39004        Roo.get(document).addKeyListener(27, function(){
39005            if(active.length > 0){
39006                hideAll();
39007            }
39008        });
39009    }
39010
39011    // private
39012    function hideAll(){
39013        if(active && active.length > 0){
39014            var c = active.clone();
39015            c.each(function(m){
39016                m.hide();
39017            });
39018        }
39019    }
39020
39021    // private
39022    function onHide(m){
39023        active.remove(m);
39024        if(active.length < 1){
39025            Roo.get(document).un("mousedown", onMouseDown);
39026            attached = false;
39027        }
39028    }
39029
39030    // private
39031    function onShow(m){
39032        var last = active.last();
39033        lastShow = new Date();
39034        active.add(m);
39035        if(!attached){
39036            Roo.get(document).on("mousedown", onMouseDown);
39037            attached = true;
39038        }
39039        if(m.parentMenu){
39040           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39041           m.parentMenu.activeChild = m;
39042        }else if(last && last.isVisible()){
39043           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39044        }
39045    }
39046
39047    // private
39048    function onBeforeHide(m){
39049        if(m.activeChild){
39050            m.activeChild.hide();
39051        }
39052        if(m.autoHideTimer){
39053            clearTimeout(m.autoHideTimer);
39054            delete m.autoHideTimer;
39055        }
39056    }
39057
39058    // private
39059    function onBeforeShow(m){
39060        var pm = m.parentMenu;
39061        if(!pm && !m.allowOtherMenus){
39062            hideAll();
39063        }else if(pm && pm.activeChild && active != m){
39064            pm.activeChild.hide();
39065        }
39066    }
39067
39068    // private
39069    function onMouseDown(e){
39070        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39071            hideAll();
39072        }
39073    }
39074
39075    // private
39076    function onBeforeCheck(mi, state){
39077        if(state){
39078            var g = groups[mi.group];
39079            for(var i = 0, l = g.length; i < l; i++){
39080                if(g[i] != mi){
39081                    g[i].setChecked(false);
39082                }
39083            }
39084        }
39085    }
39086
39087    return {
39088
39089        /**
39090         * Hides all menus that are currently visible
39091         */
39092        hideAll : function(){
39093             hideAll();  
39094        },
39095
39096        // private
39097        register : function(menu){
39098            if(!menus){
39099                init();
39100            }
39101            menus[menu.id] = menu;
39102            menu.on("beforehide", onBeforeHide);
39103            menu.on("hide", onHide);
39104            menu.on("beforeshow", onBeforeShow);
39105            menu.on("show", onShow);
39106            var g = menu.group;
39107            if(g && menu.events["checkchange"]){
39108                if(!groups[g]){
39109                    groups[g] = [];
39110                }
39111                groups[g].push(menu);
39112                menu.on("checkchange", onCheck);
39113            }
39114        },
39115
39116         /**
39117          * Returns a {@link Roo.menu.Menu} object
39118          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39119          * be used to generate and return a new Menu instance.
39120          */
39121        get : function(menu){
39122            if(typeof menu == "string"){ // menu id
39123                return menus[menu];
39124            }else if(menu.events){  // menu instance
39125                return menu;
39126            }else if(typeof menu.length == 'number'){ // array of menu items?
39127                return new Roo.menu.Menu({items:menu});
39128            }else{ // otherwise, must be a config
39129                return new Roo.menu.Menu(menu);
39130            }
39131        },
39132
39133        // private
39134        unregister : function(menu){
39135            delete menus[menu.id];
39136            menu.un("beforehide", onBeforeHide);
39137            menu.un("hide", onHide);
39138            menu.un("beforeshow", onBeforeShow);
39139            menu.un("show", onShow);
39140            var g = menu.group;
39141            if(g && menu.events["checkchange"]){
39142                groups[g].remove(menu);
39143                menu.un("checkchange", onCheck);
39144            }
39145        },
39146
39147        // private
39148        registerCheckable : function(menuItem){
39149            var g = menuItem.group;
39150            if(g){
39151                if(!groups[g]){
39152                    groups[g] = [];
39153                }
39154                groups[g].push(menuItem);
39155                menuItem.on("beforecheckchange", onBeforeCheck);
39156            }
39157        },
39158
39159        // private
39160        unregisterCheckable : function(menuItem){
39161            var g = menuItem.group;
39162            if(g){
39163                groups[g].remove(menuItem);
39164                menuItem.un("beforecheckchange", onBeforeCheck);
39165            }
39166        }
39167    };
39168 }();/*
39169  * Based on:
39170  * Ext JS Library 1.1.1
39171  * Copyright(c) 2006-2007, Ext JS, LLC.
39172  *
39173  * Originally Released Under LGPL - original licence link has changed is not relivant.
39174  *
39175  * Fork - LGPL
39176  * <script type="text/javascript">
39177  */
39178  
39179
39180 /**
39181  * @class Roo.menu.BaseItem
39182  * @extends Roo.Component
39183  * @abstract
39184  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39185  * management and base configuration options shared by all menu components.
39186  * @constructor
39187  * Creates a new BaseItem
39188  * @param {Object} config Configuration options
39189  */
39190 Roo.menu.BaseItem = function(config){
39191     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39192
39193     this.addEvents({
39194         /**
39195          * @event click
39196          * Fires when this item is clicked
39197          * @param {Roo.menu.BaseItem} this
39198          * @param {Roo.EventObject} e
39199          */
39200         click: true,
39201         /**
39202          * @event activate
39203          * Fires when this item is activated
39204          * @param {Roo.menu.BaseItem} this
39205          */
39206         activate : true,
39207         /**
39208          * @event deactivate
39209          * Fires when this item is deactivated
39210          * @param {Roo.menu.BaseItem} this
39211          */
39212         deactivate : true
39213     });
39214
39215     if(this.handler){
39216         this.on("click", this.handler, this.scope, true);
39217     }
39218 };
39219
39220 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39221     /**
39222      * @cfg {Function} handler
39223      * A function that will handle the click event of this menu item (defaults to undefined)
39224      */
39225     /**
39226      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39227      */
39228     canActivate : false,
39229     
39230      /**
39231      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39232      */
39233     hidden: false,
39234     
39235     /**
39236      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39237      */
39238     activeClass : "x-menu-item-active",
39239     /**
39240      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39241      */
39242     hideOnClick : true,
39243     /**
39244      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39245      */
39246     hideDelay : 100,
39247
39248     // private
39249     ctype: "Roo.menu.BaseItem",
39250
39251     // private
39252     actionMode : "container",
39253
39254     // private
39255     render : function(container, parentMenu){
39256         this.parentMenu = parentMenu;
39257         Roo.menu.BaseItem.superclass.render.call(this, container);
39258         this.container.menuItemId = this.id;
39259     },
39260
39261     // private
39262     onRender : function(container, position){
39263         this.el = Roo.get(this.el);
39264         container.dom.appendChild(this.el.dom);
39265     },
39266
39267     // private
39268     onClick : function(e){
39269         if(!this.disabled && this.fireEvent("click", this, e) !== false
39270                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39271             this.handleClick(e);
39272         }else{
39273             e.stopEvent();
39274         }
39275     },
39276
39277     // private
39278     activate : function(){
39279         if(this.disabled){
39280             return false;
39281         }
39282         var li = this.container;
39283         li.addClass(this.activeClass);
39284         this.region = li.getRegion().adjust(2, 2, -2, -2);
39285         this.fireEvent("activate", this);
39286         return true;
39287     },
39288
39289     // private
39290     deactivate : function(){
39291         this.container.removeClass(this.activeClass);
39292         this.fireEvent("deactivate", this);
39293     },
39294
39295     // private
39296     shouldDeactivate : function(e){
39297         return !this.region || !this.region.contains(e.getPoint());
39298     },
39299
39300     // private
39301     handleClick : function(e){
39302         if(this.hideOnClick){
39303             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39304         }
39305     },
39306
39307     // private
39308     expandMenu : function(autoActivate){
39309         // do nothing
39310     },
39311
39312     // private
39313     hideMenu : function(){
39314         // do nothing
39315     }
39316 });/*
39317  * Based on:
39318  * Ext JS Library 1.1.1
39319  * Copyright(c) 2006-2007, Ext JS, LLC.
39320  *
39321  * Originally Released Under LGPL - original licence link has changed is not relivant.
39322  *
39323  * Fork - LGPL
39324  * <script type="text/javascript">
39325  */
39326  
39327 /**
39328  * @class Roo.menu.Adapter
39329  * @extends Roo.menu.BaseItem
39330  * @abstract
39331  * 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.
39332  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39333  * @constructor
39334  * Creates a new Adapter
39335  * @param {Object} config Configuration options
39336  */
39337 Roo.menu.Adapter = function(component, config){
39338     Roo.menu.Adapter.superclass.constructor.call(this, config);
39339     this.component = component;
39340 };
39341 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39342     // private
39343     canActivate : true,
39344
39345     // private
39346     onRender : function(container, position){
39347         this.component.render(container);
39348         this.el = this.component.getEl();
39349     },
39350
39351     // private
39352     activate : function(){
39353         if(this.disabled){
39354             return false;
39355         }
39356         this.component.focus();
39357         this.fireEvent("activate", this);
39358         return true;
39359     },
39360
39361     // private
39362     deactivate : function(){
39363         this.fireEvent("deactivate", this);
39364     },
39365
39366     // private
39367     disable : function(){
39368         this.component.disable();
39369         Roo.menu.Adapter.superclass.disable.call(this);
39370     },
39371
39372     // private
39373     enable : function(){
39374         this.component.enable();
39375         Roo.menu.Adapter.superclass.enable.call(this);
39376     }
39377 });/*
39378  * Based on:
39379  * Ext JS Library 1.1.1
39380  * Copyright(c) 2006-2007, Ext JS, LLC.
39381  *
39382  * Originally Released Under LGPL - original licence link has changed is not relivant.
39383  *
39384  * Fork - LGPL
39385  * <script type="text/javascript">
39386  */
39387
39388 /**
39389  * @class Roo.menu.TextItem
39390  * @extends Roo.menu.BaseItem
39391  * Adds a static text string to a menu, usually used as either a heading or group separator.
39392  * Note: old style constructor with text is still supported.
39393  * 
39394  * @constructor
39395  * Creates a new TextItem
39396  * @param {Object} cfg Configuration
39397  */
39398 Roo.menu.TextItem = function(cfg){
39399     if (typeof(cfg) == 'string') {
39400         this.text = cfg;
39401     } else {
39402         Roo.apply(this,cfg);
39403     }
39404     
39405     Roo.menu.TextItem.superclass.constructor.call(this);
39406 };
39407
39408 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39409     /**
39410      * @cfg {String} text Text to show on item.
39411      */
39412     text : '',
39413     
39414     /**
39415      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39416      */
39417     hideOnClick : false,
39418     /**
39419      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39420      */
39421     itemCls : "x-menu-text",
39422
39423     // private
39424     onRender : function(){
39425         var s = document.createElement("span");
39426         s.className = this.itemCls;
39427         s.innerHTML = this.text;
39428         this.el = s;
39429         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39430     }
39431 });/*
39432  * Based on:
39433  * Ext JS Library 1.1.1
39434  * Copyright(c) 2006-2007, Ext JS, LLC.
39435  *
39436  * Originally Released Under LGPL - original licence link has changed is not relivant.
39437  *
39438  * Fork - LGPL
39439  * <script type="text/javascript">
39440  */
39441
39442 /**
39443  * @class Roo.menu.Separator
39444  * @extends Roo.menu.BaseItem
39445  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39446  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39447  * @constructor
39448  * @param {Object} config Configuration options
39449  */
39450 Roo.menu.Separator = function(config){
39451     Roo.menu.Separator.superclass.constructor.call(this, config);
39452 };
39453
39454 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39455     /**
39456      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39457      */
39458     itemCls : "x-menu-sep",
39459     /**
39460      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39461      */
39462     hideOnClick : false,
39463
39464     // private
39465     onRender : function(li){
39466         var s = document.createElement("span");
39467         s.className = this.itemCls;
39468         s.innerHTML = "&#160;";
39469         this.el = s;
39470         li.addClass("x-menu-sep-li");
39471         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39472     }
39473 });/*
39474  * Based on:
39475  * Ext JS Library 1.1.1
39476  * Copyright(c) 2006-2007, Ext JS, LLC.
39477  *
39478  * Originally Released Under LGPL - original licence link has changed is not relivant.
39479  *
39480  * Fork - LGPL
39481  * <script type="text/javascript">
39482  */
39483 /**
39484  * @class Roo.menu.Item
39485  * @extends Roo.menu.BaseItem
39486  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39487  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39488  * activation and click handling.
39489  * @constructor
39490  * Creates a new Item
39491  * @param {Object} config Configuration options
39492  */
39493 Roo.menu.Item = function(config){
39494     Roo.menu.Item.superclass.constructor.call(this, config);
39495     if(this.menu){
39496         this.menu = Roo.menu.MenuMgr.get(this.menu);
39497     }
39498 };
39499 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39500     /**
39501      * @cfg {Roo.menu.Menu} menu
39502      * A Sub menu
39503      */
39504     /**
39505      * @cfg {String} text
39506      * The text to show on the menu item.
39507      */
39508     text: '',
39509      /**
39510      * @cfg {String} HTML to render in menu
39511      * The text to show on the menu item (HTML version).
39512      */
39513     html: '',
39514     /**
39515      * @cfg {String} icon
39516      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39517      */
39518     icon: undefined,
39519     /**
39520      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39521      */
39522     itemCls : "x-menu-item",
39523     /**
39524      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39525      */
39526     canActivate : true,
39527     /**
39528      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39529      */
39530     showDelay: 200,
39531     // doc'd in BaseItem
39532     hideDelay: 200,
39533
39534     // private
39535     ctype: "Roo.menu.Item",
39536     
39537     // private
39538     onRender : function(container, position){
39539         var el = document.createElement("a");
39540         el.hideFocus = true;
39541         el.unselectable = "on";
39542         el.href = this.href || "#";
39543         if(this.hrefTarget){
39544             el.target = this.hrefTarget;
39545         }
39546         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39547         
39548         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39549         
39550         el.innerHTML = String.format(
39551                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39552                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39553         this.el = el;
39554         Roo.menu.Item.superclass.onRender.call(this, container, position);
39555     },
39556
39557     /**
39558      * Sets the text to display in this menu item
39559      * @param {String} text The text to display
39560      * @param {Boolean} isHTML true to indicate text is pure html.
39561      */
39562     setText : function(text, isHTML){
39563         if (isHTML) {
39564             this.html = text;
39565         } else {
39566             this.text = text;
39567             this.html = '';
39568         }
39569         if(this.rendered){
39570             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39571      
39572             this.el.update(String.format(
39573                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39574                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39575             this.parentMenu.autoWidth();
39576         }
39577     },
39578
39579     // private
39580     handleClick : function(e){
39581         if(!this.href){ // if no link defined, stop the event automatically
39582             e.stopEvent();
39583         }
39584         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39585     },
39586
39587     // private
39588     activate : function(autoExpand){
39589         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39590             this.focus();
39591             if(autoExpand){
39592                 this.expandMenu();
39593             }
39594         }
39595         return true;
39596     },
39597
39598     // private
39599     shouldDeactivate : function(e){
39600         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39601             if(this.menu && this.menu.isVisible()){
39602                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39603             }
39604             return true;
39605         }
39606         return false;
39607     },
39608
39609     // private
39610     deactivate : function(){
39611         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39612         this.hideMenu();
39613     },
39614
39615     // private
39616     expandMenu : function(autoActivate){
39617         if(!this.disabled && this.menu){
39618             clearTimeout(this.hideTimer);
39619             delete this.hideTimer;
39620             if(!this.menu.isVisible() && !this.showTimer){
39621                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39622             }else if (this.menu.isVisible() && autoActivate){
39623                 this.menu.tryActivate(0, 1);
39624             }
39625         }
39626     },
39627
39628     // private
39629     deferExpand : function(autoActivate){
39630         delete this.showTimer;
39631         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39632         if(autoActivate){
39633             this.menu.tryActivate(0, 1);
39634         }
39635     },
39636
39637     // private
39638     hideMenu : function(){
39639         clearTimeout(this.showTimer);
39640         delete this.showTimer;
39641         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39642             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39643         }
39644     },
39645
39646     // private
39647     deferHide : function(){
39648         delete this.hideTimer;
39649         this.menu.hide();
39650     }
39651 });/*
39652  * Based on:
39653  * Ext JS Library 1.1.1
39654  * Copyright(c) 2006-2007, Ext JS, LLC.
39655  *
39656  * Originally Released Under LGPL - original licence link has changed is not relivant.
39657  *
39658  * Fork - LGPL
39659  * <script type="text/javascript">
39660  */
39661  
39662 /**
39663  * @class Roo.menu.CheckItem
39664  * @extends Roo.menu.Item
39665  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39666  * @constructor
39667  * Creates a new CheckItem
39668  * @param {Object} config Configuration options
39669  */
39670 Roo.menu.CheckItem = function(config){
39671     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39672     this.addEvents({
39673         /**
39674          * @event beforecheckchange
39675          * Fires before the checked value is set, providing an opportunity to cancel if needed
39676          * @param {Roo.menu.CheckItem} this
39677          * @param {Boolean} checked The new checked value that will be set
39678          */
39679         "beforecheckchange" : true,
39680         /**
39681          * @event checkchange
39682          * Fires after the checked value has been set
39683          * @param {Roo.menu.CheckItem} this
39684          * @param {Boolean} checked The checked value that was set
39685          */
39686         "checkchange" : true
39687     });
39688     if(this.checkHandler){
39689         this.on('checkchange', this.checkHandler, this.scope);
39690     }
39691 };
39692 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39693     /**
39694      * @cfg {String} group
39695      * All check items with the same group name will automatically be grouped into a single-select
39696      * radio button group (defaults to '')
39697      */
39698     /**
39699      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39700      */
39701     itemCls : "x-menu-item x-menu-check-item",
39702     /**
39703      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39704      */
39705     groupClass : "x-menu-group-item",
39706
39707     /**
39708      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39709      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39710      * initialized with checked = true will be rendered as checked.
39711      */
39712     checked: false,
39713
39714     // private
39715     ctype: "Roo.menu.CheckItem",
39716
39717     // private
39718     onRender : function(c){
39719         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39720         if(this.group){
39721             this.el.addClass(this.groupClass);
39722         }
39723         Roo.menu.MenuMgr.registerCheckable(this);
39724         if(this.checked){
39725             this.checked = false;
39726             this.setChecked(true, true);
39727         }
39728     },
39729
39730     // private
39731     destroy : function(){
39732         if(this.rendered){
39733             Roo.menu.MenuMgr.unregisterCheckable(this);
39734         }
39735         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39736     },
39737
39738     /**
39739      * Set the checked state of this item
39740      * @param {Boolean} checked The new checked value
39741      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39742      */
39743     setChecked : function(state, suppressEvent){
39744         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39745             if(this.container){
39746                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39747             }
39748             this.checked = state;
39749             if(suppressEvent !== true){
39750                 this.fireEvent("checkchange", this, state);
39751             }
39752         }
39753     },
39754
39755     // private
39756     handleClick : function(e){
39757        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39758            this.setChecked(!this.checked);
39759        }
39760        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39761     }
39762 });/*
39763  * Based on:
39764  * Ext JS Library 1.1.1
39765  * Copyright(c) 2006-2007, Ext JS, LLC.
39766  *
39767  * Originally Released Under LGPL - original licence link has changed is not relivant.
39768  *
39769  * Fork - LGPL
39770  * <script type="text/javascript">
39771  */
39772  
39773 /**
39774  * @class Roo.menu.DateItem
39775  * @extends Roo.menu.Adapter
39776  * A menu item that wraps the {@link Roo.DatPicker} component.
39777  * @constructor
39778  * Creates a new DateItem
39779  * @param {Object} config Configuration options
39780  */
39781 Roo.menu.DateItem = function(config){
39782     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39783     /** The Roo.DatePicker object @type Roo.DatePicker */
39784     this.picker = this.component;
39785     this.addEvents({select: true});
39786     
39787     this.picker.on("render", function(picker){
39788         picker.getEl().swallowEvent("click");
39789         picker.container.addClass("x-menu-date-item");
39790     });
39791
39792     this.picker.on("select", this.onSelect, this);
39793 };
39794
39795 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39796     // private
39797     onSelect : function(picker, date){
39798         this.fireEvent("select", this, date, picker);
39799         Roo.menu.DateItem.superclass.handleClick.call(this);
39800     }
39801 });/*
39802  * Based on:
39803  * Ext JS Library 1.1.1
39804  * Copyright(c) 2006-2007, Ext JS, LLC.
39805  *
39806  * Originally Released Under LGPL - original licence link has changed is not relivant.
39807  *
39808  * Fork - LGPL
39809  * <script type="text/javascript">
39810  */
39811  
39812 /**
39813  * @class Roo.menu.ColorItem
39814  * @extends Roo.menu.Adapter
39815  * A menu item that wraps the {@link Roo.ColorPalette} component.
39816  * @constructor
39817  * Creates a new ColorItem
39818  * @param {Object} config Configuration options
39819  */
39820 Roo.menu.ColorItem = function(config){
39821     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39822     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39823     this.palette = this.component;
39824     this.relayEvents(this.palette, ["select"]);
39825     if(this.selectHandler){
39826         this.on('select', this.selectHandler, this.scope);
39827     }
39828 };
39829 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39830  * Based on:
39831  * Ext JS Library 1.1.1
39832  * Copyright(c) 2006-2007, Ext JS, LLC.
39833  *
39834  * Originally Released Under LGPL - original licence link has changed is not relivant.
39835  *
39836  * Fork - LGPL
39837  * <script type="text/javascript">
39838  */
39839  
39840
39841 /**
39842  * @class Roo.menu.DateMenu
39843  * @extends Roo.menu.Menu
39844  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39845  * @constructor
39846  * Creates a new DateMenu
39847  * @param {Object} config Configuration options
39848  */
39849 Roo.menu.DateMenu = function(config){
39850     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39851     this.plain = true;
39852     var di = new Roo.menu.DateItem(config);
39853     this.add(di);
39854     /**
39855      * The {@link Roo.DatePicker} instance for this DateMenu
39856      * @type DatePicker
39857      */
39858     this.picker = di.picker;
39859     /**
39860      * @event select
39861      * @param {DatePicker} picker
39862      * @param {Date} date
39863      */
39864     this.relayEvents(di, ["select"]);
39865     this.on('beforeshow', function(){
39866         if(this.picker){
39867             this.picker.hideMonthPicker(false);
39868         }
39869     }, this);
39870 };
39871 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39872     cls:'x-date-menu'
39873 });/*
39874  * Based on:
39875  * Ext JS Library 1.1.1
39876  * Copyright(c) 2006-2007, Ext JS, LLC.
39877  *
39878  * Originally Released Under LGPL - original licence link has changed is not relivant.
39879  *
39880  * Fork - LGPL
39881  * <script type="text/javascript">
39882  */
39883  
39884
39885 /**
39886  * @class Roo.menu.ColorMenu
39887  * @extends Roo.menu.Menu
39888  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39889  * @constructor
39890  * Creates a new ColorMenu
39891  * @param {Object} config Configuration options
39892  */
39893 Roo.menu.ColorMenu = function(config){
39894     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39895     this.plain = true;
39896     var ci = new Roo.menu.ColorItem(config);
39897     this.add(ci);
39898     /**
39899      * The {@link Roo.ColorPalette} instance for this ColorMenu
39900      * @type ColorPalette
39901      */
39902     this.palette = ci.palette;
39903     /**
39904      * @event select
39905      * @param {ColorPalette} palette
39906      * @param {String} color
39907      */
39908     this.relayEvents(ci, ["select"]);
39909 };
39910 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39911  * Based on:
39912  * Ext JS Library 1.1.1
39913  * Copyright(c) 2006-2007, Ext JS, LLC.
39914  *
39915  * Originally Released Under LGPL - original licence link has changed is not relivant.
39916  *
39917  * Fork - LGPL
39918  * <script type="text/javascript">
39919  */
39920  
39921 /**
39922  * @class Roo.form.TextItem
39923  * @extends Roo.BoxComponent
39924  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39925  * @constructor
39926  * Creates a new TextItem
39927  * @param {Object} config Configuration options
39928  */
39929 Roo.form.TextItem = function(config){
39930     Roo.form.TextItem.superclass.constructor.call(this, config);
39931 };
39932
39933 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39934     
39935     /**
39936      * @cfg {String} tag the tag for this item (default div)
39937      */
39938     tag : 'div',
39939     /**
39940      * @cfg {String} html the content for this item
39941      */
39942     html : '',
39943     
39944     getAutoCreate : function()
39945     {
39946         var cfg = {
39947             id: this.id,
39948             tag: this.tag,
39949             html: this.html,
39950             cls: 'x-form-item'
39951         };
39952         
39953         return cfg;
39954         
39955     },
39956     
39957     onRender : function(ct, position)
39958     {
39959         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39960         
39961         if(!this.el){
39962             var cfg = this.getAutoCreate();
39963             if(!cfg.name){
39964                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39965             }
39966             if (!cfg.name.length) {
39967                 delete cfg.name;
39968             }
39969             this.el = ct.createChild(cfg, position);
39970         }
39971     },
39972     /*
39973      * setHTML
39974      * @param {String} html update the Contents of the element.
39975      */
39976     setHTML : function(html)
39977     {
39978         this.fieldEl.dom.innerHTML = html;
39979     }
39980     
39981 });/*
39982  * Based on:
39983  * Ext JS Library 1.1.1
39984  * Copyright(c) 2006-2007, Ext JS, LLC.
39985  *
39986  * Originally Released Under LGPL - original licence link has changed is not relivant.
39987  *
39988  * Fork - LGPL
39989  * <script type="text/javascript">
39990  */
39991  
39992 /**
39993  * @class Roo.form.Field
39994  * @extends Roo.BoxComponent
39995  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39996  * @constructor
39997  * Creates a new Field
39998  * @param {Object} config Configuration options
39999  */
40000 Roo.form.Field = function(config){
40001     Roo.form.Field.superclass.constructor.call(this, config);
40002 };
40003
40004 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40005     /**
40006      * @cfg {String} fieldLabel Label to use when rendering a form.
40007      */
40008        /**
40009      * @cfg {String} qtip Mouse over tip
40010      */
40011      
40012     /**
40013      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40014      */
40015     invalidClass : "x-form-invalid",
40016     /**
40017      * @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")
40018      */
40019     invalidText : "The value in this field is invalid",
40020     /**
40021      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40022      */
40023     focusClass : "x-form-focus",
40024     /**
40025      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40026       automatic validation (defaults to "keyup").
40027      */
40028     validationEvent : "keyup",
40029     /**
40030      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40031      */
40032     validateOnBlur : true,
40033     /**
40034      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40035      */
40036     validationDelay : 250,
40037     /**
40038      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40039      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40040      */
40041     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40042     /**
40043      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40044      */
40045     fieldClass : "x-form-field",
40046     /**
40047      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40048      *<pre>
40049 Value         Description
40050 -----------   ----------------------------------------------------------------------
40051 qtip          Display a quick tip when the user hovers over the field
40052 title         Display a default browser title attribute popup
40053 under         Add a block div beneath the field containing the error text
40054 side          Add an error icon to the right of the field with a popup on hover
40055 [element id]  Add the error text directly to the innerHTML of the specified element
40056 </pre>
40057      */
40058     msgTarget : 'qtip',
40059     /**
40060      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40061      */
40062     msgFx : 'normal',
40063
40064     /**
40065      * @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.
40066      */
40067     readOnly : false,
40068
40069     /**
40070      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40071      */
40072     disabled : false,
40073
40074     /**
40075      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40076      */
40077     inputType : undefined,
40078     
40079     /**
40080      * @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).
40081          */
40082         tabIndex : undefined,
40083         
40084     // private
40085     isFormField : true,
40086
40087     // private
40088     hasFocus : false,
40089     /**
40090      * @property {Roo.Element} fieldEl
40091      * Element Containing the rendered Field (with label etc.)
40092      */
40093     /**
40094      * @cfg {Mixed} value A value to initialize this field with.
40095      */
40096     value : undefined,
40097
40098     /**
40099      * @cfg {String} name The field's HTML name attribute.
40100      */
40101     /**
40102      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40103      */
40104     // private
40105     loadedValue : false,
40106      
40107      
40108         // private ??
40109         initComponent : function(){
40110         Roo.form.Field.superclass.initComponent.call(this);
40111         this.addEvents({
40112             /**
40113              * @event focus
40114              * Fires when this field receives input focus.
40115              * @param {Roo.form.Field} this
40116              */
40117             focus : true,
40118             /**
40119              * @event blur
40120              * Fires when this field loses input focus.
40121              * @param {Roo.form.Field} this
40122              */
40123             blur : true,
40124             /**
40125              * @event specialkey
40126              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40127              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40128              * @param {Roo.form.Field} this
40129              * @param {Roo.EventObject} e The event object
40130              */
40131             specialkey : true,
40132             /**
40133              * @event change
40134              * Fires just before the field blurs if the field value has changed.
40135              * @param {Roo.form.Field} this
40136              * @param {Mixed} newValue The new value
40137              * @param {Mixed} oldValue The original value
40138              */
40139             change : true,
40140             /**
40141              * @event invalid
40142              * Fires after the field has been marked as invalid.
40143              * @param {Roo.form.Field} this
40144              * @param {String} msg The validation message
40145              */
40146             invalid : true,
40147             /**
40148              * @event valid
40149              * Fires after the field has been validated with no errors.
40150              * @param {Roo.form.Field} this
40151              */
40152             valid : true,
40153              /**
40154              * @event keyup
40155              * Fires after the key up
40156              * @param {Roo.form.Field} this
40157              * @param {Roo.EventObject}  e The event Object
40158              */
40159             keyup : true
40160         });
40161     },
40162
40163     /**
40164      * Returns the name attribute of the field if available
40165      * @return {String} name The field name
40166      */
40167     getName: function(){
40168          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40169     },
40170
40171     // private
40172     onRender : function(ct, position){
40173         Roo.form.Field.superclass.onRender.call(this, ct, position);
40174         if(!this.el){
40175             var cfg = this.getAutoCreate();
40176             if(!cfg.name){
40177                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40178             }
40179             if (!cfg.name.length) {
40180                 delete cfg.name;
40181             }
40182             if(this.inputType){
40183                 cfg.type = this.inputType;
40184             }
40185             this.el = ct.createChild(cfg, position);
40186         }
40187         var type = this.el.dom.type;
40188         if(type){
40189             if(type == 'password'){
40190                 type = 'text';
40191             }
40192             this.el.addClass('x-form-'+type);
40193         }
40194         if(this.readOnly){
40195             this.el.dom.readOnly = true;
40196         }
40197         if(this.tabIndex !== undefined){
40198             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40199         }
40200
40201         this.el.addClass([this.fieldClass, this.cls]);
40202         this.initValue();
40203     },
40204
40205     /**
40206      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40207      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40208      * @return {Roo.form.Field} this
40209      */
40210     applyTo : function(target){
40211         this.allowDomMove = false;
40212         this.el = Roo.get(target);
40213         this.render(this.el.dom.parentNode);
40214         return this;
40215     },
40216
40217     // private
40218     initValue : function(){
40219         if(this.value !== undefined){
40220             this.setValue(this.value);
40221         }else if(this.el.dom.value.length > 0){
40222             this.setValue(this.el.dom.value);
40223         }
40224     },
40225
40226     /**
40227      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40228      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40229      */
40230     isDirty : function() {
40231         if(this.disabled) {
40232             return false;
40233         }
40234         return String(this.getValue()) !== String(this.originalValue);
40235     },
40236
40237     /**
40238      * stores the current value in loadedValue
40239      */
40240     resetHasChanged : function()
40241     {
40242         this.loadedValue = String(this.getValue());
40243     },
40244     /**
40245      * checks the current value against the 'loaded' value.
40246      * Note - will return false if 'resetHasChanged' has not been called first.
40247      */
40248     hasChanged : function()
40249     {
40250         if(this.disabled || this.readOnly) {
40251             return false;
40252         }
40253         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40254     },
40255     
40256     
40257     
40258     // private
40259     afterRender : function(){
40260         Roo.form.Field.superclass.afterRender.call(this);
40261         this.initEvents();
40262     },
40263
40264     // private
40265     fireKey : function(e){
40266         //Roo.log('field ' + e.getKey());
40267         if(e.isNavKeyPress()){
40268             this.fireEvent("specialkey", this, e);
40269         }
40270     },
40271
40272     /**
40273      * Resets the current field value to the originally loaded value and clears any validation messages
40274      */
40275     reset : function(){
40276         this.setValue(this.resetValue);
40277         this.originalValue = this.getValue();
40278         this.clearInvalid();
40279     },
40280
40281     // private
40282     initEvents : function(){
40283         // safari killled keypress - so keydown is now used..
40284         this.el.on("keydown" , this.fireKey,  this);
40285         this.el.on("focus", this.onFocus,  this);
40286         this.el.on("blur", this.onBlur,  this);
40287         this.el.relayEvent('keyup', this);
40288
40289         // reference to original value for reset
40290         this.originalValue = this.getValue();
40291         this.resetValue =  this.getValue();
40292     },
40293
40294     // private
40295     onFocus : function(){
40296         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40297             this.el.addClass(this.focusClass);
40298         }
40299         if(!this.hasFocus){
40300             this.hasFocus = true;
40301             this.startValue = this.getValue();
40302             this.fireEvent("focus", this);
40303         }
40304     },
40305
40306     beforeBlur : Roo.emptyFn,
40307
40308     // private
40309     onBlur : function(){
40310         this.beforeBlur();
40311         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40312             this.el.removeClass(this.focusClass);
40313         }
40314         this.hasFocus = false;
40315         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40316             this.validate();
40317         }
40318         var v = this.getValue();
40319         if(String(v) !== String(this.startValue)){
40320             this.fireEvent('change', this, v, this.startValue);
40321         }
40322         this.fireEvent("blur", this);
40323     },
40324
40325     /**
40326      * Returns whether or not the field value is currently valid
40327      * @param {Boolean} preventMark True to disable marking the field invalid
40328      * @return {Boolean} True if the value is valid, else false
40329      */
40330     isValid : function(preventMark){
40331         if(this.disabled){
40332             return true;
40333         }
40334         var restore = this.preventMark;
40335         this.preventMark = preventMark === true;
40336         var v = this.validateValue(this.processValue(this.getRawValue()));
40337         this.preventMark = restore;
40338         return v;
40339     },
40340
40341     /**
40342      * Validates the field value
40343      * @return {Boolean} True if the value is valid, else false
40344      */
40345     validate : function(){
40346         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40347             this.clearInvalid();
40348             return true;
40349         }
40350         return false;
40351     },
40352
40353     processValue : function(value){
40354         return value;
40355     },
40356
40357     // private
40358     // Subclasses should provide the validation implementation by overriding this
40359     validateValue : function(value){
40360         return true;
40361     },
40362
40363     /**
40364      * Mark this field as invalid
40365      * @param {String} msg The validation message
40366      */
40367     markInvalid : function(msg){
40368         if(!this.rendered || this.preventMark){ // not rendered
40369             return;
40370         }
40371         
40372         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40373         
40374         obj.el.addClass(this.invalidClass);
40375         msg = msg || this.invalidText;
40376         switch(this.msgTarget){
40377             case 'qtip':
40378                 obj.el.dom.qtip = msg;
40379                 obj.el.dom.qclass = 'x-form-invalid-tip';
40380                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40381                     Roo.QuickTips.enable();
40382                 }
40383                 break;
40384             case 'title':
40385                 this.el.dom.title = msg;
40386                 break;
40387             case 'under':
40388                 if(!this.errorEl){
40389                     var elp = this.el.findParent('.x-form-element', 5, true);
40390                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40391                     this.errorEl.setWidth(elp.getWidth(true)-20);
40392                 }
40393                 this.errorEl.update(msg);
40394                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40395                 break;
40396             case 'side':
40397                 if(!this.errorIcon){
40398                     var elp = this.el.findParent('.x-form-element', 5, true);
40399                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40400                 }
40401                 this.alignErrorIcon();
40402                 this.errorIcon.dom.qtip = msg;
40403                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40404                 this.errorIcon.show();
40405                 this.on('resize', this.alignErrorIcon, this);
40406                 break;
40407             default:
40408                 var t = Roo.getDom(this.msgTarget);
40409                 t.innerHTML = msg;
40410                 t.style.display = this.msgDisplay;
40411                 break;
40412         }
40413         this.fireEvent('invalid', this, msg);
40414     },
40415
40416     // private
40417     alignErrorIcon : function(){
40418         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40419     },
40420
40421     /**
40422      * Clear any invalid styles/messages for this field
40423      */
40424     clearInvalid : function(){
40425         if(!this.rendered || this.preventMark){ // not rendered
40426             return;
40427         }
40428         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40429         
40430         obj.el.removeClass(this.invalidClass);
40431         switch(this.msgTarget){
40432             case 'qtip':
40433                 obj.el.dom.qtip = '';
40434                 break;
40435             case 'title':
40436                 this.el.dom.title = '';
40437                 break;
40438             case 'under':
40439                 if(this.errorEl){
40440                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40441                 }
40442                 break;
40443             case 'side':
40444                 if(this.errorIcon){
40445                     this.errorIcon.dom.qtip = '';
40446                     this.errorIcon.hide();
40447                     this.un('resize', this.alignErrorIcon, this);
40448                 }
40449                 break;
40450             default:
40451                 var t = Roo.getDom(this.msgTarget);
40452                 t.innerHTML = '';
40453                 t.style.display = 'none';
40454                 break;
40455         }
40456         this.fireEvent('valid', this);
40457     },
40458
40459     /**
40460      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40461      * @return {Mixed} value The field value
40462      */
40463     getRawValue : function(){
40464         var v = this.el.getValue();
40465         
40466         return v;
40467     },
40468
40469     /**
40470      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40471      * @return {Mixed} value The field value
40472      */
40473     getValue : function(){
40474         var v = this.el.getValue();
40475          
40476         return v;
40477     },
40478
40479     /**
40480      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40481      * @param {Mixed} value The value to set
40482      */
40483     setRawValue : function(v){
40484         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40485     },
40486
40487     /**
40488      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40489      * @param {Mixed} value The value to set
40490      */
40491     setValue : function(v){
40492         this.value = v;
40493         if(this.rendered){
40494             this.el.dom.value = (v === null || v === undefined ? '' : v);
40495              this.validate();
40496         }
40497     },
40498
40499     adjustSize : function(w, h){
40500         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40501         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40502         return s;
40503     },
40504
40505     adjustWidth : function(tag, w){
40506         tag = tag.toLowerCase();
40507         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40508             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40509                 if(tag == 'input'){
40510                     return w + 2;
40511                 }
40512                 if(tag == 'textarea'){
40513                     return w-2;
40514                 }
40515             }else if(Roo.isOpera){
40516                 if(tag == 'input'){
40517                     return w + 2;
40518                 }
40519                 if(tag == 'textarea'){
40520                     return w-2;
40521                 }
40522             }
40523         }
40524         return w;
40525     }
40526 });
40527
40528
40529 // anything other than normal should be considered experimental
40530 Roo.form.Field.msgFx = {
40531     normal : {
40532         show: function(msgEl, f){
40533             msgEl.setDisplayed('block');
40534         },
40535
40536         hide : function(msgEl, f){
40537             msgEl.setDisplayed(false).update('');
40538         }
40539     },
40540
40541     slide : {
40542         show: function(msgEl, f){
40543             msgEl.slideIn('t', {stopFx:true});
40544         },
40545
40546         hide : function(msgEl, f){
40547             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40548         }
40549     },
40550
40551     slideRight : {
40552         show: function(msgEl, f){
40553             msgEl.fixDisplay();
40554             msgEl.alignTo(f.el, 'tl-tr');
40555             msgEl.slideIn('l', {stopFx:true});
40556         },
40557
40558         hide : function(msgEl, f){
40559             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40560         }
40561     }
40562 };/*
40563  * Based on:
40564  * Ext JS Library 1.1.1
40565  * Copyright(c) 2006-2007, Ext JS, LLC.
40566  *
40567  * Originally Released Under LGPL - original licence link has changed is not relivant.
40568  *
40569  * Fork - LGPL
40570  * <script type="text/javascript">
40571  */
40572  
40573
40574 /**
40575  * @class Roo.form.TextField
40576  * @extends Roo.form.Field
40577  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40578  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40579  * @constructor
40580  * Creates a new TextField
40581  * @param {Object} config Configuration options
40582  */
40583 Roo.form.TextField = function(config){
40584     Roo.form.TextField.superclass.constructor.call(this, config);
40585     this.addEvents({
40586         /**
40587          * @event autosize
40588          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40589          * according to the default logic, but this event provides a hook for the developer to apply additional
40590          * logic at runtime to resize the field if needed.
40591              * @param {Roo.form.Field} this This text field
40592              * @param {Number} width The new field width
40593              */
40594         autosize : true
40595     });
40596 };
40597
40598 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40599     /**
40600      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40601      */
40602     grow : false,
40603     /**
40604      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40605      */
40606     growMin : 30,
40607     /**
40608      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40609      */
40610     growMax : 800,
40611     /**
40612      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40613      */
40614     vtype : null,
40615     /**
40616      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40617      */
40618     maskRe : null,
40619     /**
40620      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40621      */
40622     disableKeyFilter : false,
40623     /**
40624      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40625      */
40626     allowBlank : true,
40627     /**
40628      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40629      */
40630     minLength : 0,
40631     /**
40632      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40633      */
40634     maxLength : Number.MAX_VALUE,
40635     /**
40636      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40637      */
40638     minLengthText : "The minimum length for this field is {0}",
40639     /**
40640      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40641      */
40642     maxLengthText : "The maximum length for this field is {0}",
40643     /**
40644      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40645      */
40646     selectOnFocus : false,
40647     /**
40648      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40649      */    
40650     allowLeadingSpace : false,
40651     /**
40652      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40653      */
40654     blankText : "This field is required",
40655     /**
40656      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40657      * If available, this function will be called only after the basic validators all return true, and will be passed the
40658      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40659      */
40660     validator : null,
40661     /**
40662      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40663      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40664      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40665      */
40666     regex : null,
40667     /**
40668      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40669      */
40670     regexText : "",
40671     /**
40672      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40673      */
40674     emptyText : null,
40675    
40676
40677     // private
40678     initEvents : function()
40679     {
40680         if (this.emptyText) {
40681             this.el.attr('placeholder', this.emptyText);
40682         }
40683         
40684         Roo.form.TextField.superclass.initEvents.call(this);
40685         if(this.validationEvent == 'keyup'){
40686             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40687             this.el.on('keyup', this.filterValidation, this);
40688         }
40689         else if(this.validationEvent !== false){
40690             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40691         }
40692         
40693         if(this.selectOnFocus){
40694             this.on("focus", this.preFocus, this);
40695         }
40696         if (!this.allowLeadingSpace) {
40697             this.on('blur', this.cleanLeadingSpace, this);
40698         }
40699         
40700         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40701             this.el.on("keypress", this.filterKeys, this);
40702         }
40703         if(this.grow){
40704             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40705             this.el.on("click", this.autoSize,  this);
40706         }
40707         if(this.el.is('input[type=password]') && Roo.isSafari){
40708             this.el.on('keydown', this.SafariOnKeyDown, this);
40709         }
40710     },
40711
40712     processValue : function(value){
40713         if(this.stripCharsRe){
40714             var newValue = value.replace(this.stripCharsRe, '');
40715             if(newValue !== value){
40716                 this.setRawValue(newValue);
40717                 return newValue;
40718             }
40719         }
40720         return value;
40721     },
40722
40723     filterValidation : function(e){
40724         if(!e.isNavKeyPress()){
40725             this.validationTask.delay(this.validationDelay);
40726         }
40727     },
40728
40729     // private
40730     onKeyUp : function(e){
40731         if(!e.isNavKeyPress()){
40732             this.autoSize();
40733         }
40734     },
40735     // private - clean the leading white space
40736     cleanLeadingSpace : function(e)
40737     {
40738         if ( this.inputType == 'file') {
40739             return;
40740         }
40741         
40742         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40743     },
40744     /**
40745      * Resets the current field value to the originally-loaded value and clears any validation messages.
40746      *  
40747      */
40748     reset : function(){
40749         Roo.form.TextField.superclass.reset.call(this);
40750        
40751     }, 
40752     // private
40753     preFocus : function(){
40754         
40755         if(this.selectOnFocus){
40756             this.el.dom.select();
40757         }
40758     },
40759
40760     
40761     // private
40762     filterKeys : function(e){
40763         var k = e.getKey();
40764         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40765             return;
40766         }
40767         var c = e.getCharCode(), cc = String.fromCharCode(c);
40768         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40769             return;
40770         }
40771         if(!this.maskRe.test(cc)){
40772             e.stopEvent();
40773         }
40774     },
40775
40776     setValue : function(v){
40777         
40778         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40779         
40780         this.autoSize();
40781     },
40782
40783     /**
40784      * Validates a value according to the field's validation rules and marks the field as invalid
40785      * if the validation fails
40786      * @param {Mixed} value The value to validate
40787      * @return {Boolean} True if the value is valid, else false
40788      */
40789     validateValue : function(value){
40790         if(value.length < 1)  { // if it's blank
40791              if(this.allowBlank){
40792                 this.clearInvalid();
40793                 return true;
40794              }else{
40795                 this.markInvalid(this.blankText);
40796                 return false;
40797              }
40798         }
40799         if(value.length < this.minLength){
40800             this.markInvalid(String.format(this.minLengthText, this.minLength));
40801             return false;
40802         }
40803         if(value.length > this.maxLength){
40804             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40805             return false;
40806         }
40807         if(this.vtype){
40808             var vt = Roo.form.VTypes;
40809             if(!vt[this.vtype](value, this)){
40810                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40811                 return false;
40812             }
40813         }
40814         if(typeof this.validator == "function"){
40815             var msg = this.validator(value);
40816             if(msg !== true){
40817                 this.markInvalid(msg);
40818                 return false;
40819             }
40820         }
40821         if(this.regex && !this.regex.test(value)){
40822             this.markInvalid(this.regexText);
40823             return false;
40824         }
40825         return true;
40826     },
40827
40828     /**
40829      * Selects text in this field
40830      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40831      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40832      */
40833     selectText : function(start, end){
40834         var v = this.getRawValue();
40835         if(v.length > 0){
40836             start = start === undefined ? 0 : start;
40837             end = end === undefined ? v.length : end;
40838             var d = this.el.dom;
40839             if(d.setSelectionRange){
40840                 d.setSelectionRange(start, end);
40841             }else if(d.createTextRange){
40842                 var range = d.createTextRange();
40843                 range.moveStart("character", start);
40844                 range.moveEnd("character", v.length-end);
40845                 range.select();
40846             }
40847         }
40848     },
40849
40850     /**
40851      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40852      * This only takes effect if grow = true, and fires the autosize event.
40853      */
40854     autoSize : function(){
40855         if(!this.grow || !this.rendered){
40856             return;
40857         }
40858         if(!this.metrics){
40859             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40860         }
40861         var el = this.el;
40862         var v = el.dom.value;
40863         var d = document.createElement('div');
40864         d.appendChild(document.createTextNode(v));
40865         v = d.innerHTML;
40866         d = null;
40867         v += "&#160;";
40868         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40869         this.el.setWidth(w);
40870         this.fireEvent("autosize", this, w);
40871     },
40872     
40873     // private
40874     SafariOnKeyDown : function(event)
40875     {
40876         // this is a workaround for a password hang bug on chrome/ webkit.
40877         
40878         var isSelectAll = false;
40879         
40880         if(this.el.dom.selectionEnd > 0){
40881             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40882         }
40883         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40884             event.preventDefault();
40885             this.setValue('');
40886             return;
40887         }
40888         
40889         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40890             
40891             event.preventDefault();
40892             // this is very hacky as keydown always get's upper case.
40893             
40894             var cc = String.fromCharCode(event.getCharCode());
40895             
40896             
40897             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40898             
40899         }
40900         
40901         
40902     }
40903 });/*
40904  * Based on:
40905  * Ext JS Library 1.1.1
40906  * Copyright(c) 2006-2007, Ext JS, LLC.
40907  *
40908  * Originally Released Under LGPL - original licence link has changed is not relivant.
40909  *
40910  * Fork - LGPL
40911  * <script type="text/javascript">
40912  */
40913  
40914 /**
40915  * @class Roo.form.Hidden
40916  * @extends Roo.form.TextField
40917  * Simple Hidden element used on forms 
40918  * 
40919  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40920  * 
40921  * @constructor
40922  * Creates a new Hidden form element.
40923  * @param {Object} config Configuration options
40924  */
40925
40926
40927
40928 // easy hidden field...
40929 Roo.form.Hidden = function(config){
40930     Roo.form.Hidden.superclass.constructor.call(this, config);
40931 };
40932   
40933 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40934     fieldLabel:      '',
40935     inputType:      'hidden',
40936     width:          50,
40937     allowBlank:     true,
40938     labelSeparator: '',
40939     hidden:         true,
40940     itemCls :       'x-form-item-display-none'
40941
40942
40943 });
40944
40945
40946 /*
40947  * Based on:
40948  * Ext JS Library 1.1.1
40949  * Copyright(c) 2006-2007, Ext JS, LLC.
40950  *
40951  * Originally Released Under LGPL - original licence link has changed is not relivant.
40952  *
40953  * Fork - LGPL
40954  * <script type="text/javascript">
40955  */
40956  
40957 /**
40958  * @class Roo.form.TriggerField
40959  * @extends Roo.form.TextField
40960  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40961  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40962  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40963  * for which you can provide a custom implementation.  For example:
40964  * <pre><code>
40965 var trigger = new Roo.form.TriggerField();
40966 trigger.onTriggerClick = myTriggerFn;
40967 trigger.applyTo('my-field');
40968 </code></pre>
40969  *
40970  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40971  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40972  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40973  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40974  * @constructor
40975  * Create a new TriggerField.
40976  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40977  * to the base TextField)
40978  */
40979 Roo.form.TriggerField = function(config){
40980     this.mimicing = false;
40981     Roo.form.TriggerField.superclass.constructor.call(this, config);
40982 };
40983
40984 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40985     /**
40986      * @cfg {String} triggerClass A CSS class to apply to the trigger
40987      */
40988     /**
40989      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40990      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40991      */
40992     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40993     /**
40994      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40995      */
40996     hideTrigger:false,
40997
40998     /** @cfg {Boolean} grow @hide */
40999     /** @cfg {Number} growMin @hide */
41000     /** @cfg {Number} growMax @hide */
41001
41002     /**
41003      * @hide 
41004      * @method
41005      */
41006     autoSize: Roo.emptyFn,
41007     // private
41008     monitorTab : true,
41009     // private
41010     deferHeight : true,
41011
41012     
41013     actionMode : 'wrap',
41014     // private
41015     onResize : function(w, h){
41016         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41017         if(typeof w == 'number'){
41018             var x = w - this.trigger.getWidth();
41019             this.el.setWidth(this.adjustWidth('input', x));
41020             this.trigger.setStyle('left', x+'px');
41021         }
41022     },
41023
41024     // private
41025     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41026
41027     // private
41028     getResizeEl : function(){
41029         return this.wrap;
41030     },
41031
41032     // private
41033     getPositionEl : function(){
41034         return this.wrap;
41035     },
41036
41037     // private
41038     alignErrorIcon : function(){
41039         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41040     },
41041
41042     // private
41043     onRender : function(ct, position){
41044         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41045         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41046         this.trigger = this.wrap.createChild(this.triggerConfig ||
41047                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41048         if(this.hideTrigger){
41049             this.trigger.setDisplayed(false);
41050         }
41051         this.initTrigger();
41052         if(!this.width){
41053             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41054         }
41055     },
41056
41057     // private
41058     initTrigger : function(){
41059         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41060         this.trigger.addClassOnOver('x-form-trigger-over');
41061         this.trigger.addClassOnClick('x-form-trigger-click');
41062     },
41063
41064     // private
41065     onDestroy : function(){
41066         if(this.trigger){
41067             this.trigger.removeAllListeners();
41068             this.trigger.remove();
41069         }
41070         if(this.wrap){
41071             this.wrap.remove();
41072         }
41073         Roo.form.TriggerField.superclass.onDestroy.call(this);
41074     },
41075
41076     // private
41077     onFocus : function(){
41078         Roo.form.TriggerField.superclass.onFocus.call(this);
41079         if(!this.mimicing){
41080             this.wrap.addClass('x-trigger-wrap-focus');
41081             this.mimicing = true;
41082             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41083             if(this.monitorTab){
41084                 this.el.on("keydown", this.checkTab, this);
41085             }
41086         }
41087     },
41088
41089     // private
41090     checkTab : function(e){
41091         if(e.getKey() == e.TAB){
41092             this.triggerBlur();
41093         }
41094     },
41095
41096     // private
41097     onBlur : function(){
41098         // do nothing
41099     },
41100
41101     // private
41102     mimicBlur : function(e, t){
41103         if(!this.wrap.contains(t) && this.validateBlur()){
41104             this.triggerBlur();
41105         }
41106     },
41107
41108     // private
41109     triggerBlur : function(){
41110         this.mimicing = false;
41111         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41112         if(this.monitorTab){
41113             this.el.un("keydown", this.checkTab, this);
41114         }
41115         this.wrap.removeClass('x-trigger-wrap-focus');
41116         Roo.form.TriggerField.superclass.onBlur.call(this);
41117     },
41118
41119     // private
41120     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41121     validateBlur : function(e, t){
41122         return true;
41123     },
41124
41125     // private
41126     onDisable : function(){
41127         Roo.form.TriggerField.superclass.onDisable.call(this);
41128         if(this.wrap){
41129             this.wrap.addClass('x-item-disabled');
41130         }
41131     },
41132
41133     // private
41134     onEnable : function(){
41135         Roo.form.TriggerField.superclass.onEnable.call(this);
41136         if(this.wrap){
41137             this.wrap.removeClass('x-item-disabled');
41138         }
41139     },
41140
41141     // private
41142     onShow : function(){
41143         var ae = this.getActionEl();
41144         
41145         if(ae){
41146             ae.dom.style.display = '';
41147             ae.dom.style.visibility = 'visible';
41148         }
41149     },
41150
41151     // private
41152     
41153     onHide : function(){
41154         var ae = this.getActionEl();
41155         ae.dom.style.display = 'none';
41156     },
41157
41158     /**
41159      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41160      * by an implementing function.
41161      * @method
41162      * @param {EventObject} e
41163      */
41164     onTriggerClick : Roo.emptyFn
41165 });
41166
41167 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41168 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41169 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41170 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41171     initComponent : function(){
41172         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41173
41174         this.triggerConfig = {
41175             tag:'span', cls:'x-form-twin-triggers', cn:[
41176             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41177             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41178         ]};
41179     },
41180
41181     getTrigger : function(index){
41182         return this.triggers[index];
41183     },
41184
41185     initTrigger : function(){
41186         var ts = this.trigger.select('.x-form-trigger', true);
41187         this.wrap.setStyle('overflow', 'hidden');
41188         var triggerField = this;
41189         ts.each(function(t, all, index){
41190             t.hide = function(){
41191                 var w = triggerField.wrap.getWidth();
41192                 this.dom.style.display = 'none';
41193                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41194             };
41195             t.show = function(){
41196                 var w = triggerField.wrap.getWidth();
41197                 this.dom.style.display = '';
41198                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41199             };
41200             var triggerIndex = 'Trigger'+(index+1);
41201
41202             if(this['hide'+triggerIndex]){
41203                 t.dom.style.display = 'none';
41204             }
41205             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41206             t.addClassOnOver('x-form-trigger-over');
41207             t.addClassOnClick('x-form-trigger-click');
41208         }, this);
41209         this.triggers = ts.elements;
41210     },
41211
41212     onTrigger1Click : Roo.emptyFn,
41213     onTrigger2Click : Roo.emptyFn
41214 });/*
41215  * Based on:
41216  * Ext JS Library 1.1.1
41217  * Copyright(c) 2006-2007, Ext JS, LLC.
41218  *
41219  * Originally Released Under LGPL - original licence link has changed is not relivant.
41220  *
41221  * Fork - LGPL
41222  * <script type="text/javascript">
41223  */
41224  
41225 /**
41226  * @class Roo.form.TextArea
41227  * @extends Roo.form.TextField
41228  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41229  * support for auto-sizing.
41230  * @constructor
41231  * Creates a new TextArea
41232  * @param {Object} config Configuration options
41233  */
41234 Roo.form.TextArea = function(config){
41235     Roo.form.TextArea.superclass.constructor.call(this, config);
41236     // these are provided exchanges for backwards compat
41237     // minHeight/maxHeight were replaced by growMin/growMax to be
41238     // compatible with TextField growing config values
41239     if(this.minHeight !== undefined){
41240         this.growMin = this.minHeight;
41241     }
41242     if(this.maxHeight !== undefined){
41243         this.growMax = this.maxHeight;
41244     }
41245 };
41246
41247 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41248     /**
41249      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41250      */
41251     growMin : 60,
41252     /**
41253      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41254      */
41255     growMax: 1000,
41256     /**
41257      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41258      * in the field (equivalent to setting overflow: hidden, defaults to false)
41259      */
41260     preventScrollbars: false,
41261     /**
41262      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41263      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41264      */
41265
41266     // private
41267     onRender : function(ct, position){
41268         if(!this.el){
41269             this.defaultAutoCreate = {
41270                 tag: "textarea",
41271                 style:"width:300px;height:60px;",
41272                 autocomplete: "new-password"
41273             };
41274         }
41275         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41276         if(this.grow){
41277             this.textSizeEl = Roo.DomHelper.append(document.body, {
41278                 tag: "pre", cls: "x-form-grow-sizer"
41279             });
41280             if(this.preventScrollbars){
41281                 this.el.setStyle("overflow", "hidden");
41282             }
41283             this.el.setHeight(this.growMin);
41284         }
41285     },
41286
41287     onDestroy : function(){
41288         if(this.textSizeEl){
41289             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41290         }
41291         Roo.form.TextArea.superclass.onDestroy.call(this);
41292     },
41293
41294     // private
41295     onKeyUp : function(e){
41296         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41297             this.autoSize();
41298         }
41299     },
41300
41301     /**
41302      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41303      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41304      */
41305     autoSize : function(){
41306         if(!this.grow || !this.textSizeEl){
41307             return;
41308         }
41309         var el = this.el;
41310         var v = el.dom.value;
41311         var ts = this.textSizeEl;
41312
41313         ts.innerHTML = '';
41314         ts.appendChild(document.createTextNode(v));
41315         v = ts.innerHTML;
41316
41317         Roo.fly(ts).setWidth(this.el.getWidth());
41318         if(v.length < 1){
41319             v = "&#160;&#160;";
41320         }else{
41321             if(Roo.isIE){
41322                 v = v.replace(/\n/g, '<p>&#160;</p>');
41323             }
41324             v += "&#160;\n&#160;";
41325         }
41326         ts.innerHTML = v;
41327         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41328         if(h != this.lastHeight){
41329             this.lastHeight = h;
41330             this.el.setHeight(h);
41331             this.fireEvent("autosize", this, h);
41332         }
41333     }
41334 });/*
41335  * Based on:
41336  * Ext JS Library 1.1.1
41337  * Copyright(c) 2006-2007, Ext JS, LLC.
41338  *
41339  * Originally Released Under LGPL - original licence link has changed is not relivant.
41340  *
41341  * Fork - LGPL
41342  * <script type="text/javascript">
41343  */
41344  
41345
41346 /**
41347  * @class Roo.form.NumberField
41348  * @extends Roo.form.TextField
41349  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41350  * @constructor
41351  * Creates a new NumberField
41352  * @param {Object} config Configuration options
41353  */
41354 Roo.form.NumberField = function(config){
41355     Roo.form.NumberField.superclass.constructor.call(this, config);
41356 };
41357
41358 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41359     /**
41360      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41361      */
41362     fieldClass: "x-form-field x-form-num-field",
41363     /**
41364      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41365      */
41366     allowDecimals : true,
41367     /**
41368      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41369      */
41370     decimalSeparator : ".",
41371     /**
41372      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41373      */
41374     decimalPrecision : 2,
41375     /**
41376      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41377      */
41378     allowNegative : true,
41379     /**
41380      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41381      */
41382     minValue : Number.NEGATIVE_INFINITY,
41383     /**
41384      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41385      */
41386     maxValue : Number.MAX_VALUE,
41387     /**
41388      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41389      */
41390     minText : "The minimum value for this field is {0}",
41391     /**
41392      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41393      */
41394     maxText : "The maximum value for this field is {0}",
41395     /**
41396      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41397      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41398      */
41399     nanText : "{0} is not a valid number",
41400
41401     // private
41402     initEvents : function(){
41403         Roo.form.NumberField.superclass.initEvents.call(this);
41404         var allowed = "0123456789";
41405         if(this.allowDecimals){
41406             allowed += this.decimalSeparator;
41407         }
41408         if(this.allowNegative){
41409             allowed += "-";
41410         }
41411         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41412         var keyPress = function(e){
41413             var k = e.getKey();
41414             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41415                 return;
41416             }
41417             var c = e.getCharCode();
41418             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41419                 e.stopEvent();
41420             }
41421         };
41422         this.el.on("keypress", keyPress, this);
41423     },
41424
41425     // private
41426     validateValue : function(value){
41427         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41428             return false;
41429         }
41430         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41431              return true;
41432         }
41433         var num = this.parseValue(value);
41434         if(isNaN(num)){
41435             this.markInvalid(String.format(this.nanText, value));
41436             return false;
41437         }
41438         if(num < this.minValue){
41439             this.markInvalid(String.format(this.minText, this.minValue));
41440             return false;
41441         }
41442         if(num > this.maxValue){
41443             this.markInvalid(String.format(this.maxText, this.maxValue));
41444             return false;
41445         }
41446         return true;
41447     },
41448
41449     getValue : function(){
41450         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41451     },
41452
41453     // private
41454     parseValue : function(value){
41455         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41456         return isNaN(value) ? '' : value;
41457     },
41458
41459     // private
41460     fixPrecision : function(value){
41461         var nan = isNaN(value);
41462         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41463             return nan ? '' : value;
41464         }
41465         return parseFloat(value).toFixed(this.decimalPrecision);
41466     },
41467
41468     setValue : function(v){
41469         v = this.fixPrecision(v);
41470         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41471     },
41472
41473     // private
41474     decimalPrecisionFcn : function(v){
41475         return Math.floor(v);
41476     },
41477
41478     beforeBlur : function(){
41479         var v = this.parseValue(this.getRawValue());
41480         if(v){
41481             this.setValue(v);
41482         }
41483     }
41484 });/*
41485  * Based on:
41486  * Ext JS Library 1.1.1
41487  * Copyright(c) 2006-2007, Ext JS, LLC.
41488  *
41489  * Originally Released Under LGPL - original licence link has changed is not relivant.
41490  *
41491  * Fork - LGPL
41492  * <script type="text/javascript">
41493  */
41494  
41495 /**
41496  * @class Roo.form.DateField
41497  * @extends Roo.form.TriggerField
41498  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41499 * @constructor
41500 * Create a new DateField
41501 * @param {Object} config
41502  */
41503 Roo.form.DateField = function(config)
41504 {
41505     Roo.form.DateField.superclass.constructor.call(this, config);
41506     
41507       this.addEvents({
41508          
41509         /**
41510          * @event select
41511          * Fires when a date is selected
41512              * @param {Roo.form.DateField} combo This combo box
41513              * @param {Date} date The date selected
41514              */
41515         'select' : true
41516          
41517     });
41518     
41519     
41520     if(typeof this.minValue == "string") {
41521         this.minValue = this.parseDate(this.minValue);
41522     }
41523     if(typeof this.maxValue == "string") {
41524         this.maxValue = this.parseDate(this.maxValue);
41525     }
41526     this.ddMatch = null;
41527     if(this.disabledDates){
41528         var dd = this.disabledDates;
41529         var re = "(?:";
41530         for(var i = 0; i < dd.length; i++){
41531             re += dd[i];
41532             if(i != dd.length-1) {
41533                 re += "|";
41534             }
41535         }
41536         this.ddMatch = new RegExp(re + ")");
41537     }
41538 };
41539
41540 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41541     /**
41542      * @cfg {String} format
41543      * The default date format string which can be overriden for localization support.  The format must be
41544      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41545      */
41546     format : "m/d/y",
41547     /**
41548      * @cfg {String} altFormats
41549      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41550      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41551      */
41552     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41553     /**
41554      * @cfg {Array} disabledDays
41555      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41556      */
41557     disabledDays : null,
41558     /**
41559      * @cfg {String} disabledDaysText
41560      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41561      */
41562     disabledDaysText : "Disabled",
41563     /**
41564      * @cfg {Array} disabledDates
41565      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41566      * expression so they are very powerful. Some examples:
41567      * <ul>
41568      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41569      * <li>["03/08", "09/16"] would disable those days for every year</li>
41570      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41571      * <li>["03/../2006"] would disable every day in March 2006</li>
41572      * <li>["^03"] would disable every day in every March</li>
41573      * </ul>
41574      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41575      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41576      */
41577     disabledDates : null,
41578     /**
41579      * @cfg {String} disabledDatesText
41580      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41581      */
41582     disabledDatesText : "Disabled",
41583         
41584         
41585         /**
41586      * @cfg {Date/String} zeroValue
41587      * if the date is less that this number, then the field is rendered as empty
41588      * default is 1800
41589      */
41590         zeroValue : '1800-01-01',
41591         
41592         
41593     /**
41594      * @cfg {Date/String} minValue
41595      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41596      * valid format (defaults to null).
41597      */
41598     minValue : null,
41599     /**
41600      * @cfg {Date/String} maxValue
41601      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41602      * valid format (defaults to null).
41603      */
41604     maxValue : null,
41605     /**
41606      * @cfg {String} minText
41607      * The error text to display when the date in the cell is before minValue (defaults to
41608      * 'The date in this field must be after {minValue}').
41609      */
41610     minText : "The date in this field must be equal to or after {0}",
41611     /**
41612      * @cfg {String} maxText
41613      * The error text to display when the date in the cell is after maxValue (defaults to
41614      * 'The date in this field must be before {maxValue}').
41615      */
41616     maxText : "The date in this field must be equal to or before {0}",
41617     /**
41618      * @cfg {String} invalidText
41619      * The error text to display when the date in the field is invalid (defaults to
41620      * '{value} is not a valid date - it must be in the format {format}').
41621      */
41622     invalidText : "{0} is not a valid date - it must be in the format {1}",
41623     /**
41624      * @cfg {String} triggerClass
41625      * An additional CSS class used to style the trigger button.  The trigger will always get the
41626      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41627      * which displays a calendar icon).
41628      */
41629     triggerClass : 'x-form-date-trigger',
41630     
41631
41632     /**
41633      * @cfg {Boolean} useIso
41634      * if enabled, then the date field will use a hidden field to store the 
41635      * real value as iso formated date. default (false)
41636      */ 
41637     useIso : false,
41638     /**
41639      * @cfg {String/Object} autoCreate
41640      * A DomHelper element spec, or true for a default element spec (defaults to
41641      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41642      */ 
41643     // private
41644     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41645     
41646     // private
41647     hiddenField: false,
41648     
41649     onRender : function(ct, position)
41650     {
41651         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41652         if (this.useIso) {
41653             //this.el.dom.removeAttribute('name'); 
41654             Roo.log("Changing name?");
41655             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41656             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41657                     'before', true);
41658             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41659             // prevent input submission
41660             this.hiddenName = this.name;
41661         }
41662             
41663             
41664     },
41665     
41666     // private
41667     validateValue : function(value)
41668     {
41669         value = this.formatDate(value);
41670         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41671             Roo.log('super failed');
41672             return false;
41673         }
41674         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41675              return true;
41676         }
41677         var svalue = value;
41678         value = this.parseDate(value);
41679         if(!value){
41680             Roo.log('parse date failed' + svalue);
41681             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41682             return false;
41683         }
41684         var time = value.getTime();
41685         if(this.minValue && time < this.minValue.getTime()){
41686             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41687             return false;
41688         }
41689         if(this.maxValue && time > this.maxValue.getTime()){
41690             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41691             return false;
41692         }
41693         if(this.disabledDays){
41694             var day = value.getDay();
41695             for(var i = 0; i < this.disabledDays.length; i++) {
41696                 if(day === this.disabledDays[i]){
41697                     this.markInvalid(this.disabledDaysText);
41698                     return false;
41699                 }
41700             }
41701         }
41702         var fvalue = this.formatDate(value);
41703         if(this.ddMatch && this.ddMatch.test(fvalue)){
41704             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41705             return false;
41706         }
41707         return true;
41708     },
41709
41710     // private
41711     // Provides logic to override the default TriggerField.validateBlur which just returns true
41712     validateBlur : function(){
41713         return !this.menu || !this.menu.isVisible();
41714     },
41715     
41716     getName: function()
41717     {
41718         // returns hidden if it's set..
41719         if (!this.rendered) {return ''};
41720         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41721         
41722     },
41723
41724     /**
41725      * Returns the current date value of the date field.
41726      * @return {Date} The date value
41727      */
41728     getValue : function(){
41729         
41730         return  this.hiddenField ?
41731                 this.hiddenField.value :
41732                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41733     },
41734
41735     /**
41736      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41737      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41738      * (the default format used is "m/d/y").
41739      * <br />Usage:
41740      * <pre><code>
41741 //All of these calls set the same date value (May 4, 2006)
41742
41743 //Pass a date object:
41744 var dt = new Date('5/4/06');
41745 dateField.setValue(dt);
41746
41747 //Pass a date string (default format):
41748 dateField.setValue('5/4/06');
41749
41750 //Pass a date string (custom format):
41751 dateField.format = 'Y-m-d';
41752 dateField.setValue('2006-5-4');
41753 </code></pre>
41754      * @param {String/Date} date The date or valid date string
41755      */
41756     setValue : function(date){
41757         if (this.hiddenField) {
41758             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41759         }
41760         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41761         // make sure the value field is always stored as a date..
41762         this.value = this.parseDate(date);
41763         
41764         
41765     },
41766
41767     // private
41768     parseDate : function(value){
41769                 
41770                 if (value instanceof Date) {
41771                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41772                                 return  '';
41773                         }
41774                         return value;
41775                 }
41776                 
41777                 
41778         if(!value || value instanceof Date){
41779             return value;
41780         }
41781         var v = Date.parseDate(value, this.format);
41782          if (!v && this.useIso) {
41783             v = Date.parseDate(value, 'Y-m-d');
41784         }
41785         if(!v && this.altFormats){
41786             if(!this.altFormatsArray){
41787                 this.altFormatsArray = this.altFormats.split("|");
41788             }
41789             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41790                 v = Date.parseDate(value, this.altFormatsArray[i]);
41791             }
41792         }
41793                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41794                         v = '';
41795                 }
41796         return v;
41797     },
41798
41799     // private
41800     formatDate : function(date, fmt){
41801         return (!date || !(date instanceof Date)) ?
41802                date : date.dateFormat(fmt || this.format);
41803     },
41804
41805     // private
41806     menuListeners : {
41807         select: function(m, d){
41808             
41809             this.setValue(d);
41810             this.fireEvent('select', this, d);
41811         },
41812         show : function(){ // retain focus styling
41813             this.onFocus();
41814         },
41815         hide : function(){
41816             this.focus.defer(10, this);
41817             var ml = this.menuListeners;
41818             this.menu.un("select", ml.select,  this);
41819             this.menu.un("show", ml.show,  this);
41820             this.menu.un("hide", ml.hide,  this);
41821         }
41822     },
41823
41824     // private
41825     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41826     onTriggerClick : function(){
41827         if(this.disabled){
41828             return;
41829         }
41830         if(this.menu == null){
41831             this.menu = new Roo.menu.DateMenu();
41832         }
41833         Roo.apply(this.menu.picker,  {
41834             showClear: this.allowBlank,
41835             minDate : this.minValue,
41836             maxDate : this.maxValue,
41837             disabledDatesRE : this.ddMatch,
41838             disabledDatesText : this.disabledDatesText,
41839             disabledDays : this.disabledDays,
41840             disabledDaysText : this.disabledDaysText,
41841             format : this.useIso ? 'Y-m-d' : this.format,
41842             minText : String.format(this.minText, this.formatDate(this.minValue)),
41843             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41844         });
41845         this.menu.on(Roo.apply({}, this.menuListeners, {
41846             scope:this
41847         }));
41848         this.menu.picker.setValue(this.getValue() || new Date());
41849         this.menu.show(this.el, "tl-bl?");
41850     },
41851
41852     beforeBlur : function(){
41853         var v = this.parseDate(this.getRawValue());
41854         if(v){
41855             this.setValue(v);
41856         }
41857     },
41858
41859     /*@
41860      * overide
41861      * 
41862      */
41863     isDirty : function() {
41864         if(this.disabled) {
41865             return false;
41866         }
41867         
41868         if(typeof(this.startValue) === 'undefined'){
41869             return false;
41870         }
41871         
41872         return String(this.getValue()) !== String(this.startValue);
41873         
41874     },
41875     // @overide
41876     cleanLeadingSpace : function(e)
41877     {
41878        return;
41879     }
41880     
41881 });/*
41882  * Based on:
41883  * Ext JS Library 1.1.1
41884  * Copyright(c) 2006-2007, Ext JS, LLC.
41885  *
41886  * Originally Released Under LGPL - original licence link has changed is not relivant.
41887  *
41888  * Fork - LGPL
41889  * <script type="text/javascript">
41890  */
41891  
41892 /**
41893  * @class Roo.form.MonthField
41894  * @extends Roo.form.TriggerField
41895  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41896 * @constructor
41897 * Create a new MonthField
41898 * @param {Object} config
41899  */
41900 Roo.form.MonthField = function(config){
41901     
41902     Roo.form.MonthField.superclass.constructor.call(this, config);
41903     
41904       this.addEvents({
41905          
41906         /**
41907          * @event select
41908          * Fires when a date is selected
41909              * @param {Roo.form.MonthFieeld} combo This combo box
41910              * @param {Date} date The date selected
41911              */
41912         'select' : true
41913          
41914     });
41915     
41916     
41917     if(typeof this.minValue == "string") {
41918         this.minValue = this.parseDate(this.minValue);
41919     }
41920     if(typeof this.maxValue == "string") {
41921         this.maxValue = this.parseDate(this.maxValue);
41922     }
41923     this.ddMatch = null;
41924     if(this.disabledDates){
41925         var dd = this.disabledDates;
41926         var re = "(?:";
41927         for(var i = 0; i < dd.length; i++){
41928             re += dd[i];
41929             if(i != dd.length-1) {
41930                 re += "|";
41931             }
41932         }
41933         this.ddMatch = new RegExp(re + ")");
41934     }
41935 };
41936
41937 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41938     /**
41939      * @cfg {String} format
41940      * The default date format string which can be overriden for localization support.  The format must be
41941      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41942      */
41943     format : "M Y",
41944     /**
41945      * @cfg {String} altFormats
41946      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41947      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41948      */
41949     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41950     /**
41951      * @cfg {Array} disabledDays
41952      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41953      */
41954     disabledDays : [0,1,2,3,4,5,6],
41955     /**
41956      * @cfg {String} disabledDaysText
41957      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41958      */
41959     disabledDaysText : "Disabled",
41960     /**
41961      * @cfg {Array} disabledDates
41962      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41963      * expression so they are very powerful. Some examples:
41964      * <ul>
41965      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41966      * <li>["03/08", "09/16"] would disable those days for every year</li>
41967      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41968      * <li>["03/../2006"] would disable every day in March 2006</li>
41969      * <li>["^03"] would disable every day in every March</li>
41970      * </ul>
41971      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41972      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41973      */
41974     disabledDates : null,
41975     /**
41976      * @cfg {String} disabledDatesText
41977      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41978      */
41979     disabledDatesText : "Disabled",
41980     /**
41981      * @cfg {Date/String} minValue
41982      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41983      * valid format (defaults to null).
41984      */
41985     minValue : null,
41986     /**
41987      * @cfg {Date/String} maxValue
41988      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41989      * valid format (defaults to null).
41990      */
41991     maxValue : null,
41992     /**
41993      * @cfg {String} minText
41994      * The error text to display when the date in the cell is before minValue (defaults to
41995      * 'The date in this field must be after {minValue}').
41996      */
41997     minText : "The date in this field must be equal to or after {0}",
41998     /**
41999      * @cfg {String} maxTextf
42000      * The error text to display when the date in the cell is after maxValue (defaults to
42001      * 'The date in this field must be before {maxValue}').
42002      */
42003     maxText : "The date in this field must be equal to or before {0}",
42004     /**
42005      * @cfg {String} invalidText
42006      * The error text to display when the date in the field is invalid (defaults to
42007      * '{value} is not a valid date - it must be in the format {format}').
42008      */
42009     invalidText : "{0} is not a valid date - it must be in the format {1}",
42010     /**
42011      * @cfg {String} triggerClass
42012      * An additional CSS class used to style the trigger button.  The trigger will always get the
42013      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42014      * which displays a calendar icon).
42015      */
42016     triggerClass : 'x-form-date-trigger',
42017     
42018
42019     /**
42020      * @cfg {Boolean} useIso
42021      * if enabled, then the date field will use a hidden field to store the 
42022      * real value as iso formated date. default (true)
42023      */ 
42024     useIso : true,
42025     /**
42026      * @cfg {String/Object} autoCreate
42027      * A DomHelper element spec, or true for a default element spec (defaults to
42028      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42029      */ 
42030     // private
42031     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42032     
42033     // private
42034     hiddenField: false,
42035     
42036     hideMonthPicker : false,
42037     
42038     onRender : function(ct, position)
42039     {
42040         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42041         if (this.useIso) {
42042             this.el.dom.removeAttribute('name'); 
42043             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42044                     'before', true);
42045             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42046             // prevent input submission
42047             this.hiddenName = this.name;
42048         }
42049             
42050             
42051     },
42052     
42053     // private
42054     validateValue : function(value)
42055     {
42056         value = this.formatDate(value);
42057         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42058             return false;
42059         }
42060         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42061              return true;
42062         }
42063         var svalue = value;
42064         value = this.parseDate(value);
42065         if(!value){
42066             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42067             return false;
42068         }
42069         var time = value.getTime();
42070         if(this.minValue && time < this.minValue.getTime()){
42071             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42072             return false;
42073         }
42074         if(this.maxValue && time > this.maxValue.getTime()){
42075             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42076             return false;
42077         }
42078         /*if(this.disabledDays){
42079             var day = value.getDay();
42080             for(var i = 0; i < this.disabledDays.length; i++) {
42081                 if(day === this.disabledDays[i]){
42082                     this.markInvalid(this.disabledDaysText);
42083                     return false;
42084                 }
42085             }
42086         }
42087         */
42088         var fvalue = this.formatDate(value);
42089         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42090             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42091             return false;
42092         }
42093         */
42094         return true;
42095     },
42096
42097     // private
42098     // Provides logic to override the default TriggerField.validateBlur which just returns true
42099     validateBlur : function(){
42100         return !this.menu || !this.menu.isVisible();
42101     },
42102
42103     /**
42104      * Returns the current date value of the date field.
42105      * @return {Date} The date value
42106      */
42107     getValue : function(){
42108         
42109         
42110         
42111         return  this.hiddenField ?
42112                 this.hiddenField.value :
42113                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42114     },
42115
42116     /**
42117      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42118      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42119      * (the default format used is "m/d/y").
42120      * <br />Usage:
42121      * <pre><code>
42122 //All of these calls set the same date value (May 4, 2006)
42123
42124 //Pass a date object:
42125 var dt = new Date('5/4/06');
42126 monthField.setValue(dt);
42127
42128 //Pass a date string (default format):
42129 monthField.setValue('5/4/06');
42130
42131 //Pass a date string (custom format):
42132 monthField.format = 'Y-m-d';
42133 monthField.setValue('2006-5-4');
42134 </code></pre>
42135      * @param {String/Date} date The date or valid date string
42136      */
42137     setValue : function(date){
42138         Roo.log('month setValue' + date);
42139         // can only be first of month..
42140         
42141         var val = this.parseDate(date);
42142         
42143         if (this.hiddenField) {
42144             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42145         }
42146         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42147         this.value = this.parseDate(date);
42148     },
42149
42150     // private
42151     parseDate : function(value){
42152         if(!value || value instanceof Date){
42153             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42154             return value;
42155         }
42156         var v = Date.parseDate(value, this.format);
42157         if (!v && this.useIso) {
42158             v = Date.parseDate(value, 'Y-m-d');
42159         }
42160         if (v) {
42161             // 
42162             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42163         }
42164         
42165         
42166         if(!v && this.altFormats){
42167             if(!this.altFormatsArray){
42168                 this.altFormatsArray = this.altFormats.split("|");
42169             }
42170             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42171                 v = Date.parseDate(value, this.altFormatsArray[i]);
42172             }
42173         }
42174         return v;
42175     },
42176
42177     // private
42178     formatDate : function(date, fmt){
42179         return (!date || !(date instanceof Date)) ?
42180                date : date.dateFormat(fmt || this.format);
42181     },
42182
42183     // private
42184     menuListeners : {
42185         select: function(m, d){
42186             this.setValue(d);
42187             this.fireEvent('select', this, d);
42188         },
42189         show : function(){ // retain focus styling
42190             this.onFocus();
42191         },
42192         hide : function(){
42193             this.focus.defer(10, this);
42194             var ml = this.menuListeners;
42195             this.menu.un("select", ml.select,  this);
42196             this.menu.un("show", ml.show,  this);
42197             this.menu.un("hide", ml.hide,  this);
42198         }
42199     },
42200     // private
42201     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42202     onTriggerClick : function(){
42203         if(this.disabled){
42204             return;
42205         }
42206         if(this.menu == null){
42207             this.menu = new Roo.menu.DateMenu();
42208            
42209         }
42210         
42211         Roo.apply(this.menu.picker,  {
42212             
42213             showClear: this.allowBlank,
42214             minDate : this.minValue,
42215             maxDate : this.maxValue,
42216             disabledDatesRE : this.ddMatch,
42217             disabledDatesText : this.disabledDatesText,
42218             
42219             format : this.useIso ? 'Y-m-d' : this.format,
42220             minText : String.format(this.minText, this.formatDate(this.minValue)),
42221             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42222             
42223         });
42224          this.menu.on(Roo.apply({}, this.menuListeners, {
42225             scope:this
42226         }));
42227        
42228         
42229         var m = this.menu;
42230         var p = m.picker;
42231         
42232         // hide month picker get's called when we called by 'before hide';
42233         
42234         var ignorehide = true;
42235         p.hideMonthPicker  = function(disableAnim){
42236             if (ignorehide) {
42237                 return;
42238             }
42239              if(this.monthPicker){
42240                 Roo.log("hideMonthPicker called");
42241                 if(disableAnim === true){
42242                     this.monthPicker.hide();
42243                 }else{
42244                     this.monthPicker.slideOut('t', {duration:.2});
42245                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42246                     p.fireEvent("select", this, this.value);
42247                     m.hide();
42248                 }
42249             }
42250         }
42251         
42252         Roo.log('picker set value');
42253         Roo.log(this.getValue());
42254         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42255         m.show(this.el, 'tl-bl?');
42256         ignorehide  = false;
42257         // this will trigger hideMonthPicker..
42258         
42259         
42260         // hidden the day picker
42261         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42262         
42263         
42264         
42265       
42266         
42267         p.showMonthPicker.defer(100, p);
42268     
42269         
42270        
42271     },
42272
42273     beforeBlur : function(){
42274         var v = this.parseDate(this.getRawValue());
42275         if(v){
42276             this.setValue(v);
42277         }
42278     }
42279
42280     /** @cfg {Boolean} grow @hide */
42281     /** @cfg {Number} growMin @hide */
42282     /** @cfg {Number} growMax @hide */
42283     /**
42284      * @hide
42285      * @method autoSize
42286      */
42287 });/*
42288  * Based on:
42289  * Ext JS Library 1.1.1
42290  * Copyright(c) 2006-2007, Ext JS, LLC.
42291  *
42292  * Originally Released Under LGPL - original licence link has changed is not relivant.
42293  *
42294  * Fork - LGPL
42295  * <script type="text/javascript">
42296  */
42297  
42298
42299 /**
42300  * @class Roo.form.ComboBox
42301  * @extends Roo.form.TriggerField
42302  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42303  * @constructor
42304  * Create a new ComboBox.
42305  * @param {Object} config Configuration options
42306  */
42307 Roo.form.ComboBox = function(config){
42308     Roo.form.ComboBox.superclass.constructor.call(this, config);
42309     this.addEvents({
42310         /**
42311          * @event expand
42312          * Fires when the dropdown list is expanded
42313              * @param {Roo.form.ComboBox} combo This combo box
42314              */
42315         'expand' : true,
42316         /**
42317          * @event collapse
42318          * Fires when the dropdown list is collapsed
42319              * @param {Roo.form.ComboBox} combo This combo box
42320              */
42321         'collapse' : true,
42322         /**
42323          * @event beforeselect
42324          * Fires before a list item is selected. Return false to cancel the selection.
42325              * @param {Roo.form.ComboBox} combo This combo box
42326              * @param {Roo.data.Record} record The data record returned from the underlying store
42327              * @param {Number} index The index of the selected item in the dropdown list
42328              */
42329         'beforeselect' : true,
42330         /**
42331          * @event select
42332          * Fires when a list item is selected
42333              * @param {Roo.form.ComboBox} combo This combo box
42334              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42335              * @param {Number} index The index of the selected item in the dropdown list
42336              */
42337         'select' : true,
42338         /**
42339          * @event beforequery
42340          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42341          * The event object passed has these properties:
42342              * @param {Roo.form.ComboBox} combo This combo box
42343              * @param {String} query The query
42344              * @param {Boolean} forceAll true to force "all" query
42345              * @param {Boolean} cancel true to cancel the query
42346              * @param {Object} e The query event object
42347              */
42348         'beforequery': true,
42349          /**
42350          * @event add
42351          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42352              * @param {Roo.form.ComboBox} combo This combo box
42353              */
42354         'add' : true,
42355         /**
42356          * @event edit
42357          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42358              * @param {Roo.form.ComboBox} combo This combo box
42359              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42360              */
42361         'edit' : true
42362         
42363         
42364     });
42365     if(this.transform){
42366         this.allowDomMove = false;
42367         var s = Roo.getDom(this.transform);
42368         if(!this.hiddenName){
42369             this.hiddenName = s.name;
42370         }
42371         if(!this.store){
42372             this.mode = 'local';
42373             var d = [], opts = s.options;
42374             for(var i = 0, len = opts.length;i < len; i++){
42375                 var o = opts[i];
42376                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42377                 if(o.selected) {
42378                     this.value = value;
42379                 }
42380                 d.push([value, o.text]);
42381             }
42382             this.store = new Roo.data.SimpleStore({
42383                 'id': 0,
42384                 fields: ['value', 'text'],
42385                 data : d
42386             });
42387             this.valueField = 'value';
42388             this.displayField = 'text';
42389         }
42390         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42391         if(!this.lazyRender){
42392             this.target = true;
42393             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42394             s.parentNode.removeChild(s); // remove it
42395             this.render(this.el.parentNode);
42396         }else{
42397             s.parentNode.removeChild(s); // remove it
42398         }
42399
42400     }
42401     if (this.store) {
42402         this.store = Roo.factory(this.store, Roo.data);
42403     }
42404     
42405     this.selectedIndex = -1;
42406     if(this.mode == 'local'){
42407         if(config.queryDelay === undefined){
42408             this.queryDelay = 10;
42409         }
42410         if(config.minChars === undefined){
42411             this.minChars = 0;
42412         }
42413     }
42414 };
42415
42416 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42417     /**
42418      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42419      */
42420     /**
42421      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42422      * rendering into an Roo.Editor, defaults to false)
42423      */
42424     /**
42425      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42426      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42427      */
42428     /**
42429      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42430      */
42431     /**
42432      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42433      * the dropdown list (defaults to undefined, with no header element)
42434      */
42435
42436      /**
42437      * @cfg {String/Roo.Template} tpl The template to use to render the output
42438      */
42439      
42440     // private
42441     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42442     /**
42443      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42444      */
42445     listWidth: undefined,
42446     /**
42447      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42448      * mode = 'remote' or 'text' if mode = 'local')
42449      */
42450     displayField: undefined,
42451     /**
42452      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42453      * mode = 'remote' or 'value' if mode = 'local'). 
42454      * Note: use of a valueField requires the user make a selection
42455      * in order for a value to be mapped.
42456      */
42457     valueField: undefined,
42458     
42459     
42460     /**
42461      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42462      * field's data value (defaults to the underlying DOM element's name)
42463      */
42464     hiddenName: undefined,
42465     /**
42466      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42467      */
42468     listClass: '',
42469     /**
42470      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42471      */
42472     selectedClass: 'x-combo-selected',
42473     /**
42474      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42475      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42476      * which displays a downward arrow icon).
42477      */
42478     triggerClass : 'x-form-arrow-trigger',
42479     /**
42480      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42481      */
42482     shadow:'sides',
42483     /**
42484      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42485      * anchor positions (defaults to 'tl-bl')
42486      */
42487     listAlign: 'tl-bl?',
42488     /**
42489      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42490      */
42491     maxHeight: 300,
42492     /**
42493      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42494      * query specified by the allQuery config option (defaults to 'query')
42495      */
42496     triggerAction: 'query',
42497     /**
42498      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42499      * (defaults to 4, does not apply if editable = false)
42500      */
42501     minChars : 4,
42502     /**
42503      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42504      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42505      */
42506     typeAhead: false,
42507     /**
42508      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42509      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42510      */
42511     queryDelay: 500,
42512     /**
42513      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42514      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42515      */
42516     pageSize: 0,
42517     /**
42518      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42519      * when editable = true (defaults to false)
42520      */
42521     selectOnFocus:false,
42522     /**
42523      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42524      */
42525     queryParam: 'query',
42526     /**
42527      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42528      * when mode = 'remote' (defaults to 'Loading...')
42529      */
42530     loadingText: 'Loading...',
42531     /**
42532      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42533      */
42534     resizable: false,
42535     /**
42536      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42537      */
42538     handleHeight : 8,
42539     /**
42540      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42541      * traditional select (defaults to true)
42542      */
42543     editable: true,
42544     /**
42545      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42546      */
42547     allQuery: '',
42548     /**
42549      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42550      */
42551     mode: 'remote',
42552     /**
42553      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42554      * listWidth has a higher value)
42555      */
42556     minListWidth : 70,
42557     /**
42558      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42559      * allow the user to set arbitrary text into the field (defaults to false)
42560      */
42561     forceSelection:false,
42562     /**
42563      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42564      * if typeAhead = true (defaults to 250)
42565      */
42566     typeAheadDelay : 250,
42567     /**
42568      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42569      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42570      */
42571     valueNotFoundText : undefined,
42572     /**
42573      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42574      */
42575     blockFocus : false,
42576     
42577     /**
42578      * @cfg {Boolean} disableClear Disable showing of clear button.
42579      */
42580     disableClear : false,
42581     /**
42582      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42583      */
42584     alwaysQuery : false,
42585     
42586     //private
42587     addicon : false,
42588     editicon: false,
42589     
42590     // element that contains real text value.. (when hidden is used..)
42591      
42592     // private
42593     onRender : function(ct, position)
42594     {
42595         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42596         
42597         if(this.hiddenName){
42598             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42599                     'before', true);
42600             this.hiddenField.value =
42601                 this.hiddenValue !== undefined ? this.hiddenValue :
42602                 this.value !== undefined ? this.value : '';
42603
42604             // prevent input submission
42605             this.el.dom.removeAttribute('name');
42606              
42607              
42608         }
42609         
42610         if(Roo.isGecko){
42611             this.el.dom.setAttribute('autocomplete', 'off');
42612         }
42613
42614         var cls = 'x-combo-list';
42615
42616         this.list = new Roo.Layer({
42617             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42618         });
42619
42620         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42621         this.list.setWidth(lw);
42622         this.list.swallowEvent('mousewheel');
42623         this.assetHeight = 0;
42624
42625         if(this.title){
42626             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42627             this.assetHeight += this.header.getHeight();
42628         }
42629
42630         this.innerList = this.list.createChild({cls:cls+'-inner'});
42631         this.innerList.on('mouseover', this.onViewOver, this);
42632         this.innerList.on('mousemove', this.onViewMove, this);
42633         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42634         
42635         if(this.allowBlank && !this.pageSize && !this.disableClear){
42636             this.footer = this.list.createChild({cls:cls+'-ft'});
42637             this.pageTb = new Roo.Toolbar(this.footer);
42638            
42639         }
42640         if(this.pageSize){
42641             this.footer = this.list.createChild({cls:cls+'-ft'});
42642             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42643                     {pageSize: this.pageSize});
42644             
42645         }
42646         
42647         if (this.pageTb && this.allowBlank && !this.disableClear) {
42648             var _this = this;
42649             this.pageTb.add(new Roo.Toolbar.Fill(), {
42650                 cls: 'x-btn-icon x-btn-clear',
42651                 text: '&#160;',
42652                 handler: function()
42653                 {
42654                     _this.collapse();
42655                     _this.clearValue();
42656                     _this.onSelect(false, -1);
42657                 }
42658             });
42659         }
42660         if (this.footer) {
42661             this.assetHeight += this.footer.getHeight();
42662         }
42663         
42664
42665         if(!this.tpl){
42666             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42667         }
42668
42669         this.view = new Roo.View(this.innerList, this.tpl, {
42670             singleSelect:true,
42671             store: this.store,
42672             selectedClass: this.selectedClass
42673         });
42674
42675         this.view.on('click', this.onViewClick, this);
42676
42677         this.store.on('beforeload', this.onBeforeLoad, this);
42678         this.store.on('load', this.onLoad, this);
42679         this.store.on('loadexception', this.onLoadException, this);
42680
42681         if(this.resizable){
42682             this.resizer = new Roo.Resizable(this.list,  {
42683                pinned:true, handles:'se'
42684             });
42685             this.resizer.on('resize', function(r, w, h){
42686                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42687                 this.listWidth = w;
42688                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42689                 this.restrictHeight();
42690             }, this);
42691             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42692         }
42693         if(!this.editable){
42694             this.editable = true;
42695             this.setEditable(false);
42696         }  
42697         
42698         
42699         if (typeof(this.events.add.listeners) != 'undefined') {
42700             
42701             this.addicon = this.wrap.createChild(
42702                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42703        
42704             this.addicon.on('click', function(e) {
42705                 this.fireEvent('add', this);
42706             }, this);
42707         }
42708         if (typeof(this.events.edit.listeners) != 'undefined') {
42709             
42710             this.editicon = this.wrap.createChild(
42711                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42712             if (this.addicon) {
42713                 this.editicon.setStyle('margin-left', '40px');
42714             }
42715             this.editicon.on('click', function(e) {
42716                 
42717                 // we fire even  if inothing is selected..
42718                 this.fireEvent('edit', this, this.lastData );
42719                 
42720             }, this);
42721         }
42722         
42723         
42724         
42725     },
42726
42727     // private
42728     initEvents : function(){
42729         Roo.form.ComboBox.superclass.initEvents.call(this);
42730
42731         this.keyNav = new Roo.KeyNav(this.el, {
42732             "up" : function(e){
42733                 this.inKeyMode = true;
42734                 this.selectPrev();
42735             },
42736
42737             "down" : function(e){
42738                 if(!this.isExpanded()){
42739                     this.onTriggerClick();
42740                 }else{
42741                     this.inKeyMode = true;
42742                     this.selectNext();
42743                 }
42744             },
42745
42746             "enter" : function(e){
42747                 this.onViewClick();
42748                 //return true;
42749             },
42750
42751             "esc" : function(e){
42752                 this.collapse();
42753             },
42754
42755             "tab" : function(e){
42756                 this.onViewClick(false);
42757                 this.fireEvent("specialkey", this, e);
42758                 return true;
42759             },
42760
42761             scope : this,
42762
42763             doRelay : function(foo, bar, hname){
42764                 if(hname == 'down' || this.scope.isExpanded()){
42765                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42766                 }
42767                 return true;
42768             },
42769
42770             forceKeyDown: true
42771         });
42772         this.queryDelay = Math.max(this.queryDelay || 10,
42773                 this.mode == 'local' ? 10 : 250);
42774         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42775         if(this.typeAhead){
42776             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42777         }
42778         if(this.editable !== false){
42779             this.el.on("keyup", this.onKeyUp, this);
42780         }
42781         if(this.forceSelection){
42782             this.on('blur', this.doForce, this);
42783         }
42784     },
42785
42786     onDestroy : function(){
42787         if(this.view){
42788             this.view.setStore(null);
42789             this.view.el.removeAllListeners();
42790             this.view.el.remove();
42791             this.view.purgeListeners();
42792         }
42793         if(this.list){
42794             this.list.destroy();
42795         }
42796         if(this.store){
42797             this.store.un('beforeload', this.onBeforeLoad, this);
42798             this.store.un('load', this.onLoad, this);
42799             this.store.un('loadexception', this.onLoadException, this);
42800         }
42801         Roo.form.ComboBox.superclass.onDestroy.call(this);
42802     },
42803
42804     // private
42805     fireKey : function(e){
42806         if(e.isNavKeyPress() && !this.list.isVisible()){
42807             this.fireEvent("specialkey", this, e);
42808         }
42809     },
42810
42811     // private
42812     onResize: function(w, h){
42813         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42814         
42815         if(typeof w != 'number'){
42816             // we do not handle it!?!?
42817             return;
42818         }
42819         var tw = this.trigger.getWidth();
42820         tw += this.addicon ? this.addicon.getWidth() : 0;
42821         tw += this.editicon ? this.editicon.getWidth() : 0;
42822         var x = w - tw;
42823         this.el.setWidth( this.adjustWidth('input', x));
42824             
42825         this.trigger.setStyle('left', x+'px');
42826         
42827         if(this.list && this.listWidth === undefined){
42828             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42829             this.list.setWidth(lw);
42830             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42831         }
42832         
42833     
42834         
42835     },
42836
42837     /**
42838      * Allow or prevent the user from directly editing the field text.  If false is passed,
42839      * the user will only be able to select from the items defined in the dropdown list.  This method
42840      * is the runtime equivalent of setting the 'editable' config option at config time.
42841      * @param {Boolean} value True to allow the user to directly edit the field text
42842      */
42843     setEditable : function(value){
42844         if(value == this.editable){
42845             return;
42846         }
42847         this.editable = value;
42848         if(!value){
42849             this.el.dom.setAttribute('readOnly', true);
42850             this.el.on('mousedown', this.onTriggerClick,  this);
42851             this.el.addClass('x-combo-noedit');
42852         }else{
42853             this.el.dom.setAttribute('readOnly', false);
42854             this.el.un('mousedown', this.onTriggerClick,  this);
42855             this.el.removeClass('x-combo-noedit');
42856         }
42857     },
42858
42859     // private
42860     onBeforeLoad : function(){
42861         if(!this.hasFocus){
42862             return;
42863         }
42864         this.innerList.update(this.loadingText ?
42865                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42866         this.restrictHeight();
42867         this.selectedIndex = -1;
42868     },
42869
42870     // private
42871     onLoad : function(){
42872         if(!this.hasFocus){
42873             return;
42874         }
42875         if(this.store.getCount() > 0){
42876             this.expand();
42877             this.restrictHeight();
42878             if(this.lastQuery == this.allQuery){
42879                 if(this.editable){
42880                     this.el.dom.select();
42881                 }
42882                 if(!this.selectByValue(this.value, true)){
42883                     this.select(0, true);
42884                 }
42885             }else{
42886                 this.selectNext();
42887                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42888                     this.taTask.delay(this.typeAheadDelay);
42889                 }
42890             }
42891         }else{
42892             this.onEmptyResults();
42893         }
42894         //this.el.focus();
42895     },
42896     // private
42897     onLoadException : function()
42898     {
42899         this.collapse();
42900         Roo.log(this.store.reader.jsonData);
42901         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42902             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42903         }
42904         
42905         
42906     },
42907     // private
42908     onTypeAhead : function(){
42909         if(this.store.getCount() > 0){
42910             var r = this.store.getAt(0);
42911             var newValue = r.data[this.displayField];
42912             var len = newValue.length;
42913             var selStart = this.getRawValue().length;
42914             if(selStart != len){
42915                 this.setRawValue(newValue);
42916                 this.selectText(selStart, newValue.length);
42917             }
42918         }
42919     },
42920
42921     // private
42922     onSelect : function(record, index){
42923         if(this.fireEvent('beforeselect', this, record, index) !== false){
42924             this.setFromData(index > -1 ? record.data : false);
42925             this.collapse();
42926             this.fireEvent('select', this, record, index);
42927         }
42928     },
42929
42930     /**
42931      * Returns the currently selected field value or empty string if no value is set.
42932      * @return {String} value The selected value
42933      */
42934     getValue : function(){
42935         if(this.valueField){
42936             return typeof this.value != 'undefined' ? this.value : '';
42937         }
42938         return Roo.form.ComboBox.superclass.getValue.call(this);
42939     },
42940
42941     /**
42942      * Clears any text/value currently set in the field
42943      */
42944     clearValue : function(){
42945         if(this.hiddenField){
42946             this.hiddenField.value = '';
42947         }
42948         this.value = '';
42949         this.setRawValue('');
42950         this.lastSelectionText = '';
42951         
42952     },
42953
42954     /**
42955      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42956      * will be displayed in the field.  If the value does not match the data value of an existing item,
42957      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42958      * Otherwise the field will be blank (although the value will still be set).
42959      * @param {String} value The value to match
42960      */
42961     setValue : function(v){
42962         var text = v;
42963         if(this.valueField){
42964             var r = this.findRecord(this.valueField, v);
42965             if(r){
42966                 text = r.data[this.displayField];
42967             }else if(this.valueNotFoundText !== undefined){
42968                 text = this.valueNotFoundText;
42969             }
42970         }
42971         this.lastSelectionText = text;
42972         if(this.hiddenField){
42973             this.hiddenField.value = v;
42974         }
42975         Roo.form.ComboBox.superclass.setValue.call(this, text);
42976         this.value = v;
42977     },
42978     /**
42979      * @property {Object} the last set data for the element
42980      */
42981     
42982     lastData : false,
42983     /**
42984      * Sets the value of the field based on a object which is related to the record format for the store.
42985      * @param {Object} value the value to set as. or false on reset?
42986      */
42987     setFromData : function(o){
42988         var dv = ''; // display value
42989         var vv = ''; // value value..
42990         this.lastData = o;
42991         if (this.displayField) {
42992             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42993         } else {
42994             // this is an error condition!!!
42995             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42996         }
42997         
42998         if(this.valueField){
42999             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43000         }
43001         if(this.hiddenField){
43002             this.hiddenField.value = vv;
43003             
43004             this.lastSelectionText = dv;
43005             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43006             this.value = vv;
43007             return;
43008         }
43009         // no hidden field.. - we store the value in 'value', but still display
43010         // display field!!!!
43011         this.lastSelectionText = dv;
43012         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43013         this.value = vv;
43014         
43015         
43016     },
43017     // private
43018     reset : function(){
43019         // overridden so that last data is reset..
43020         this.setValue(this.resetValue);
43021         this.originalValue = this.getValue();
43022         this.clearInvalid();
43023         this.lastData = false;
43024         if (this.view) {
43025             this.view.clearSelections();
43026         }
43027     },
43028     // private
43029     findRecord : function(prop, value){
43030         var record;
43031         if(this.store.getCount() > 0){
43032             this.store.each(function(r){
43033                 if(r.data[prop] == value){
43034                     record = r;
43035                     return false;
43036                 }
43037                 return true;
43038             });
43039         }
43040         return record;
43041     },
43042     
43043     getName: function()
43044     {
43045         // returns hidden if it's set..
43046         if (!this.rendered) {return ''};
43047         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43048         
43049     },
43050     // private
43051     onViewMove : function(e, t){
43052         this.inKeyMode = false;
43053     },
43054
43055     // private
43056     onViewOver : function(e, t){
43057         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43058             return;
43059         }
43060         var item = this.view.findItemFromChild(t);
43061         if(item){
43062             var index = this.view.indexOf(item);
43063             this.select(index, false);
43064         }
43065     },
43066
43067     // private
43068     onViewClick : function(doFocus)
43069     {
43070         var index = this.view.getSelectedIndexes()[0];
43071         var r = this.store.getAt(index);
43072         if(r){
43073             this.onSelect(r, index);
43074         }
43075         if(doFocus !== false && !this.blockFocus){
43076             this.el.focus();
43077         }
43078     },
43079
43080     // private
43081     restrictHeight : function(){
43082         this.innerList.dom.style.height = '';
43083         var inner = this.innerList.dom;
43084         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43085         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43086         this.list.beginUpdate();
43087         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43088         this.list.alignTo(this.el, this.listAlign);
43089         this.list.endUpdate();
43090     },
43091
43092     // private
43093     onEmptyResults : function(){
43094         this.collapse();
43095     },
43096
43097     /**
43098      * Returns true if the dropdown list is expanded, else false.
43099      */
43100     isExpanded : function(){
43101         return this.list.isVisible();
43102     },
43103
43104     /**
43105      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43106      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43107      * @param {String} value The data value of the item to select
43108      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43109      * selected item if it is not currently in view (defaults to true)
43110      * @return {Boolean} True if the value matched an item in the list, else false
43111      */
43112     selectByValue : function(v, scrollIntoView){
43113         if(v !== undefined && v !== null){
43114             var r = this.findRecord(this.valueField || this.displayField, v);
43115             if(r){
43116                 this.select(this.store.indexOf(r), scrollIntoView);
43117                 return true;
43118             }
43119         }
43120         return false;
43121     },
43122
43123     /**
43124      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43125      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43126      * @param {Number} index The zero-based index of the list item to select
43127      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43128      * selected item if it is not currently in view (defaults to true)
43129      */
43130     select : function(index, scrollIntoView){
43131         this.selectedIndex = index;
43132         this.view.select(index);
43133         if(scrollIntoView !== false){
43134             var el = this.view.getNode(index);
43135             if(el){
43136                 this.innerList.scrollChildIntoView(el, false);
43137             }
43138         }
43139     },
43140
43141     // private
43142     selectNext : function(){
43143         var ct = this.store.getCount();
43144         if(ct > 0){
43145             if(this.selectedIndex == -1){
43146                 this.select(0);
43147             }else if(this.selectedIndex < ct-1){
43148                 this.select(this.selectedIndex+1);
43149             }
43150         }
43151     },
43152
43153     // private
43154     selectPrev : function(){
43155         var ct = this.store.getCount();
43156         if(ct > 0){
43157             if(this.selectedIndex == -1){
43158                 this.select(0);
43159             }else if(this.selectedIndex != 0){
43160                 this.select(this.selectedIndex-1);
43161             }
43162         }
43163     },
43164
43165     // private
43166     onKeyUp : function(e){
43167         if(this.editable !== false && !e.isSpecialKey()){
43168             this.lastKey = e.getKey();
43169             this.dqTask.delay(this.queryDelay);
43170         }
43171     },
43172
43173     // private
43174     validateBlur : function(){
43175         return !this.list || !this.list.isVisible();   
43176     },
43177
43178     // private
43179     initQuery : function(){
43180         this.doQuery(this.getRawValue());
43181     },
43182
43183     // private
43184     doForce : function(){
43185         if(this.el.dom.value.length > 0){
43186             this.el.dom.value =
43187                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43188              
43189         }
43190     },
43191
43192     /**
43193      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43194      * query allowing the query action to be canceled if needed.
43195      * @param {String} query The SQL query to execute
43196      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43197      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43198      * saved in the current store (defaults to false)
43199      */
43200     doQuery : function(q, forceAll){
43201         if(q === undefined || q === null){
43202             q = '';
43203         }
43204         var qe = {
43205             query: q,
43206             forceAll: forceAll,
43207             combo: this,
43208             cancel:false
43209         };
43210         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43211             return false;
43212         }
43213         q = qe.query;
43214         forceAll = qe.forceAll;
43215         if(forceAll === true || (q.length >= this.minChars)){
43216             if(this.lastQuery != q || this.alwaysQuery){
43217                 this.lastQuery = q;
43218                 if(this.mode == 'local'){
43219                     this.selectedIndex = -1;
43220                     if(forceAll){
43221                         this.store.clearFilter();
43222                     }else{
43223                         this.store.filter(this.displayField, q);
43224                     }
43225                     this.onLoad();
43226                 }else{
43227                     this.store.baseParams[this.queryParam] = q;
43228                     this.store.load({
43229                         params: this.getParams(q)
43230                     });
43231                     this.expand();
43232                 }
43233             }else{
43234                 this.selectedIndex = -1;
43235                 this.onLoad();   
43236             }
43237         }
43238     },
43239
43240     // private
43241     getParams : function(q){
43242         var p = {};
43243         //p[this.queryParam] = q;
43244         if(this.pageSize){
43245             p.start = 0;
43246             p.limit = this.pageSize;
43247         }
43248         return p;
43249     },
43250
43251     /**
43252      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43253      */
43254     collapse : function(){
43255         if(!this.isExpanded()){
43256             return;
43257         }
43258         this.list.hide();
43259         Roo.get(document).un('mousedown', this.collapseIf, this);
43260         Roo.get(document).un('mousewheel', this.collapseIf, this);
43261         if (!this.editable) {
43262             Roo.get(document).un('keydown', this.listKeyPress, this);
43263         }
43264         this.fireEvent('collapse', this);
43265     },
43266
43267     // private
43268     collapseIf : function(e){
43269         if(!e.within(this.wrap) && !e.within(this.list)){
43270             this.collapse();
43271         }
43272     },
43273
43274     /**
43275      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43276      */
43277     expand : function(){
43278         if(this.isExpanded() || !this.hasFocus){
43279             return;
43280         }
43281         this.list.alignTo(this.el, this.listAlign);
43282         this.list.show();
43283         Roo.get(document).on('mousedown', this.collapseIf, this);
43284         Roo.get(document).on('mousewheel', this.collapseIf, this);
43285         if (!this.editable) {
43286             Roo.get(document).on('keydown', this.listKeyPress, this);
43287         }
43288         
43289         this.fireEvent('expand', this);
43290     },
43291
43292     // private
43293     // Implements the default empty TriggerField.onTriggerClick function
43294     onTriggerClick : function(){
43295         if(this.disabled){
43296             return;
43297         }
43298         if(this.isExpanded()){
43299             this.collapse();
43300             if (!this.blockFocus) {
43301                 this.el.focus();
43302             }
43303             
43304         }else {
43305             this.hasFocus = true;
43306             if(this.triggerAction == 'all') {
43307                 this.doQuery(this.allQuery, true);
43308             } else {
43309                 this.doQuery(this.getRawValue());
43310             }
43311             if (!this.blockFocus) {
43312                 this.el.focus();
43313             }
43314         }
43315     },
43316     listKeyPress : function(e)
43317     {
43318         //Roo.log('listkeypress');
43319         // scroll to first matching element based on key pres..
43320         if (e.isSpecialKey()) {
43321             return false;
43322         }
43323         var k = String.fromCharCode(e.getKey()).toUpperCase();
43324         //Roo.log(k);
43325         var match  = false;
43326         var csel = this.view.getSelectedNodes();
43327         var cselitem = false;
43328         if (csel.length) {
43329             var ix = this.view.indexOf(csel[0]);
43330             cselitem  = this.store.getAt(ix);
43331             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43332                 cselitem = false;
43333             }
43334             
43335         }
43336         
43337         this.store.each(function(v) { 
43338             if (cselitem) {
43339                 // start at existing selection.
43340                 if (cselitem.id == v.id) {
43341                     cselitem = false;
43342                 }
43343                 return;
43344             }
43345                 
43346             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43347                 match = this.store.indexOf(v);
43348                 return false;
43349             }
43350         }, this);
43351         
43352         if (match === false) {
43353             return true; // no more action?
43354         }
43355         // scroll to?
43356         this.view.select(match);
43357         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43358         sn.scrollIntoView(sn.dom.parentNode, false);
43359     } 
43360
43361     /** 
43362     * @cfg {Boolean} grow 
43363     * @hide 
43364     */
43365     /** 
43366     * @cfg {Number} growMin 
43367     * @hide 
43368     */
43369     /** 
43370     * @cfg {Number} growMax 
43371     * @hide 
43372     */
43373     /**
43374      * @hide
43375      * @method autoSize
43376      */
43377 });/*
43378  * Copyright(c) 2010-2012, Roo J Solutions Limited
43379  *
43380  * Licence LGPL
43381  *
43382  */
43383
43384 /**
43385  * @class Roo.form.ComboBoxArray
43386  * @extends Roo.form.TextField
43387  * A facebook style adder... for lists of email / people / countries  etc...
43388  * pick multiple items from a combo box, and shows each one.
43389  *
43390  *  Fred [x]  Brian [x]  [Pick another |v]
43391  *
43392  *
43393  *  For this to work: it needs various extra information
43394  *    - normal combo problay has
43395  *      name, hiddenName
43396  *    + displayField, valueField
43397  *
43398  *    For our purpose...
43399  *
43400  *
43401  *   If we change from 'extends' to wrapping...
43402  *   
43403  *  
43404  *
43405  
43406  
43407  * @constructor
43408  * Create a new ComboBoxArray.
43409  * @param {Object} config Configuration options
43410  */
43411  
43412
43413 Roo.form.ComboBoxArray = function(config)
43414 {
43415     this.addEvents({
43416         /**
43417          * @event beforeremove
43418          * Fires before remove the value from the list
43419              * @param {Roo.form.ComboBoxArray} _self This combo box array
43420              * @param {Roo.form.ComboBoxArray.Item} item removed item
43421              */
43422         'beforeremove' : true,
43423         /**
43424          * @event remove
43425          * Fires when remove the value from the list
43426              * @param {Roo.form.ComboBoxArray} _self This combo box array
43427              * @param {Roo.form.ComboBoxArray.Item} item removed item
43428              */
43429         'remove' : true
43430         
43431         
43432     });
43433     
43434     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43435     
43436     this.items = new Roo.util.MixedCollection(false);
43437     
43438     // construct the child combo...
43439     
43440     
43441     
43442     
43443    
43444     
43445 }
43446
43447  
43448 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43449
43450     /**
43451      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43452      */
43453     
43454     lastData : false,
43455     
43456     // behavies liek a hiddne field
43457     inputType:      'hidden',
43458     /**
43459      * @cfg {Number} width The width of the box that displays the selected element
43460      */ 
43461     width:          300,
43462
43463     
43464     
43465     /**
43466      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43467      */
43468     name : false,
43469     /**
43470      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43471      */
43472     hiddenName : false,
43473       /**
43474      * @cfg {String} seperator    The value seperator normally ',' 
43475      */
43476     seperator : ',',
43477     
43478     // private the array of items that are displayed..
43479     items  : false,
43480     // private - the hidden field el.
43481     hiddenEl : false,
43482     // private - the filed el..
43483     el : false,
43484     
43485     //validateValue : function() { return true; }, // all values are ok!
43486     //onAddClick: function() { },
43487     
43488     onRender : function(ct, position) 
43489     {
43490         
43491         // create the standard hidden element
43492         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43493         
43494         
43495         // give fake names to child combo;
43496         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43497         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43498         
43499         this.combo = Roo.factory(this.combo, Roo.form);
43500         this.combo.onRender(ct, position);
43501         if (typeof(this.combo.width) != 'undefined') {
43502             this.combo.onResize(this.combo.width,0);
43503         }
43504         
43505         this.combo.initEvents();
43506         
43507         // assigned so form know we need to do this..
43508         this.store          = this.combo.store;
43509         this.valueField     = this.combo.valueField;
43510         this.displayField   = this.combo.displayField ;
43511         
43512         
43513         this.combo.wrap.addClass('x-cbarray-grp');
43514         
43515         var cbwrap = this.combo.wrap.createChild(
43516             {tag: 'div', cls: 'x-cbarray-cb'},
43517             this.combo.el.dom
43518         );
43519         
43520              
43521         this.hiddenEl = this.combo.wrap.createChild({
43522             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43523         });
43524         this.el = this.combo.wrap.createChild({
43525             tag: 'input',  type:'hidden' , name: this.name, value : ''
43526         });
43527          //   this.el.dom.removeAttribute("name");
43528         
43529         
43530         this.outerWrap = this.combo.wrap;
43531         this.wrap = cbwrap;
43532         
43533         this.outerWrap.setWidth(this.width);
43534         this.outerWrap.dom.removeChild(this.el.dom);
43535         
43536         this.wrap.dom.appendChild(this.el.dom);
43537         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43538         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43539         
43540         this.combo.trigger.setStyle('position','relative');
43541         this.combo.trigger.setStyle('left', '0px');
43542         this.combo.trigger.setStyle('top', '2px');
43543         
43544         this.combo.el.setStyle('vertical-align', 'text-bottom');
43545         
43546         //this.trigger.setStyle('vertical-align', 'top');
43547         
43548         // this should use the code from combo really... on('add' ....)
43549         if (this.adder) {
43550             
43551         
43552             this.adder = this.outerWrap.createChild(
43553                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43554             var _t = this;
43555             this.adder.on('click', function(e) {
43556                 _t.fireEvent('adderclick', this, e);
43557             }, _t);
43558         }
43559         //var _t = this;
43560         //this.adder.on('click', this.onAddClick, _t);
43561         
43562         
43563         this.combo.on('select', function(cb, rec, ix) {
43564             this.addItem(rec.data);
43565             
43566             cb.setValue('');
43567             cb.el.dom.value = '';
43568             //cb.lastData = rec.data;
43569             // add to list
43570             
43571         }, this);
43572         
43573         
43574     },
43575     
43576     
43577     getName: function()
43578     {
43579         // returns hidden if it's set..
43580         if (!this.rendered) {return ''};
43581         return  this.hiddenName ? this.hiddenName : this.name;
43582         
43583     },
43584     
43585     
43586     onResize: function(w, h){
43587         
43588         return;
43589         // not sure if this is needed..
43590         //this.combo.onResize(w,h);
43591         
43592         if(typeof w != 'number'){
43593             // we do not handle it!?!?
43594             return;
43595         }
43596         var tw = this.combo.trigger.getWidth();
43597         tw += this.addicon ? this.addicon.getWidth() : 0;
43598         tw += this.editicon ? this.editicon.getWidth() : 0;
43599         var x = w - tw;
43600         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43601             
43602         this.combo.trigger.setStyle('left', '0px');
43603         
43604         if(this.list && this.listWidth === undefined){
43605             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43606             this.list.setWidth(lw);
43607             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43608         }
43609         
43610     
43611         
43612     },
43613     
43614     addItem: function(rec)
43615     {
43616         var valueField = this.combo.valueField;
43617         var displayField = this.combo.displayField;
43618         
43619         if (this.items.indexOfKey(rec[valueField]) > -1) {
43620             //console.log("GOT " + rec.data.id);
43621             return;
43622         }
43623         
43624         var x = new Roo.form.ComboBoxArray.Item({
43625             //id : rec[this.idField],
43626             data : rec,
43627             displayField : displayField ,
43628             tipField : displayField ,
43629             cb : this
43630         });
43631         // use the 
43632         this.items.add(rec[valueField],x);
43633         // add it before the element..
43634         this.updateHiddenEl();
43635         x.render(this.outerWrap, this.wrap.dom);
43636         // add the image handler..
43637     },
43638     
43639     updateHiddenEl : function()
43640     {
43641         this.validate();
43642         if (!this.hiddenEl) {
43643             return;
43644         }
43645         var ar = [];
43646         var idField = this.combo.valueField;
43647         
43648         this.items.each(function(f) {
43649             ar.push(f.data[idField]);
43650         });
43651         this.hiddenEl.dom.value = ar.join(this.seperator);
43652         this.validate();
43653     },
43654     
43655     reset : function()
43656     {
43657         this.items.clear();
43658         
43659         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43660            el.remove();
43661         });
43662         
43663         this.el.dom.value = '';
43664         if (this.hiddenEl) {
43665             this.hiddenEl.dom.value = '';
43666         }
43667         
43668     },
43669     getValue: function()
43670     {
43671         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43672     },
43673     setValue: function(v) // not a valid action - must use addItems..
43674     {
43675         
43676         this.reset();
43677          
43678         if (this.store.isLocal && (typeof(v) == 'string')) {
43679             // then we can use the store to find the values..
43680             // comma seperated at present.. this needs to allow JSON based encoding..
43681             this.hiddenEl.value  = v;
43682             var v_ar = [];
43683             Roo.each(v.split(this.seperator), function(k) {
43684                 Roo.log("CHECK " + this.valueField + ',' + k);
43685                 var li = this.store.query(this.valueField, k);
43686                 if (!li.length) {
43687                     return;
43688                 }
43689                 var add = {};
43690                 add[this.valueField] = k;
43691                 add[this.displayField] = li.item(0).data[this.displayField];
43692                 
43693                 this.addItem(add);
43694             }, this) 
43695              
43696         }
43697         if (typeof(v) == 'object' ) {
43698             // then let's assume it's an array of objects..
43699             Roo.each(v, function(l) {
43700                 var add = l;
43701                 if (typeof(l) == 'string') {
43702                     add = {};
43703                     add[this.valueField] = l;
43704                     add[this.displayField] = l
43705                 }
43706                 this.addItem(add);
43707             }, this);
43708              
43709         }
43710         
43711         
43712     },
43713     setFromData: function(v)
43714     {
43715         // this recieves an object, if setValues is called.
43716         this.reset();
43717         this.el.dom.value = v[this.displayField];
43718         this.hiddenEl.dom.value = v[this.valueField];
43719         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43720             return;
43721         }
43722         var kv = v[this.valueField];
43723         var dv = v[this.displayField];
43724         kv = typeof(kv) != 'string' ? '' : kv;
43725         dv = typeof(dv) != 'string' ? '' : dv;
43726         
43727         
43728         var keys = kv.split(this.seperator);
43729         var display = dv.split(this.seperator);
43730         for (var i = 0 ; i < keys.length; i++) {
43731             add = {};
43732             add[this.valueField] = keys[i];
43733             add[this.displayField] = display[i];
43734             this.addItem(add);
43735         }
43736       
43737         
43738     },
43739     
43740     /**
43741      * Validates the combox array value
43742      * @return {Boolean} True if the value is valid, else false
43743      */
43744     validate : function(){
43745         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43746             this.clearInvalid();
43747             return true;
43748         }
43749         return false;
43750     },
43751     
43752     validateValue : function(value){
43753         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43754         
43755     },
43756     
43757     /*@
43758      * overide
43759      * 
43760      */
43761     isDirty : function() {
43762         if(this.disabled) {
43763             return false;
43764         }
43765         
43766         try {
43767             var d = Roo.decode(String(this.originalValue));
43768         } catch (e) {
43769             return String(this.getValue()) !== String(this.originalValue);
43770         }
43771         
43772         var originalValue = [];
43773         
43774         for (var i = 0; i < d.length; i++){
43775             originalValue.push(d[i][this.valueField]);
43776         }
43777         
43778         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43779         
43780     }
43781     
43782 });
43783
43784
43785
43786 /**
43787  * @class Roo.form.ComboBoxArray.Item
43788  * @extends Roo.BoxComponent
43789  * A selected item in the list
43790  *  Fred [x]  Brian [x]  [Pick another |v]
43791  * 
43792  * @constructor
43793  * Create a new item.
43794  * @param {Object} config Configuration options
43795  */
43796  
43797 Roo.form.ComboBoxArray.Item = function(config) {
43798     config.id = Roo.id();
43799     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43800 }
43801
43802 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43803     data : {},
43804     cb: false,
43805     displayField : false,
43806     tipField : false,
43807     
43808     
43809     defaultAutoCreate : {
43810         tag: 'div',
43811         cls: 'x-cbarray-item',
43812         cn : [ 
43813             { tag: 'div' },
43814             {
43815                 tag: 'img',
43816                 width:16,
43817                 height : 16,
43818                 src : Roo.BLANK_IMAGE_URL ,
43819                 align: 'center'
43820             }
43821         ]
43822         
43823     },
43824     
43825  
43826     onRender : function(ct, position)
43827     {
43828         Roo.form.Field.superclass.onRender.call(this, ct, position);
43829         
43830         if(!this.el){
43831             var cfg = this.getAutoCreate();
43832             this.el = ct.createChild(cfg, position);
43833         }
43834         
43835         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43836         
43837         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43838             this.cb.renderer(this.data) :
43839             String.format('{0}',this.data[this.displayField]);
43840         
43841             
43842         this.el.child('div').dom.setAttribute('qtip',
43843                         String.format('{0}',this.data[this.tipField])
43844         );
43845         
43846         this.el.child('img').on('click', this.remove, this);
43847         
43848     },
43849    
43850     remove : function()
43851     {
43852         if(this.cb.disabled){
43853             return;
43854         }
43855         
43856         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43857             this.cb.items.remove(this);
43858             this.el.child('img').un('click', this.remove, this);
43859             this.el.remove();
43860             this.cb.updateHiddenEl();
43861
43862             this.cb.fireEvent('remove', this.cb, this);
43863         }
43864         
43865     }
43866 });/*
43867  * RooJS Library 1.1.1
43868  * Copyright(c) 2008-2011  Alan Knowles
43869  *
43870  * License - LGPL
43871  */
43872  
43873
43874 /**
43875  * @class Roo.form.ComboNested
43876  * @extends Roo.form.ComboBox
43877  * A combobox for that allows selection of nested items in a list,
43878  * eg.
43879  *
43880  *  Book
43881  *    -> red
43882  *    -> green
43883  *  Table
43884  *    -> square
43885  *      ->red
43886  *      ->green
43887  *    -> rectangle
43888  *      ->green
43889  *      
43890  * 
43891  * @constructor
43892  * Create a new ComboNested
43893  * @param {Object} config Configuration options
43894  */
43895 Roo.form.ComboNested = function(config){
43896     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43897     // should verify some data...
43898     // like
43899     // hiddenName = required..
43900     // displayField = required
43901     // valudField == required
43902     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43903     var _t = this;
43904     Roo.each(req, function(e) {
43905         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43906             throw "Roo.form.ComboNested : missing value for: " + e;
43907         }
43908     });
43909      
43910     
43911 };
43912
43913 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43914    
43915     /*
43916      * @config {Number} max Number of columns to show
43917      */
43918     
43919     maxColumns : 3,
43920    
43921     list : null, // the outermost div..
43922     innerLists : null, // the
43923     views : null,
43924     stores : null,
43925     // private
43926     loadingChildren : false,
43927     
43928     onRender : function(ct, position)
43929     {
43930         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43931         
43932         if(this.hiddenName){
43933             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43934                     'before', true);
43935             this.hiddenField.value =
43936                 this.hiddenValue !== undefined ? this.hiddenValue :
43937                 this.value !== undefined ? this.value : '';
43938
43939             // prevent input submission
43940             this.el.dom.removeAttribute('name');
43941              
43942              
43943         }
43944         
43945         if(Roo.isGecko){
43946             this.el.dom.setAttribute('autocomplete', 'off');
43947         }
43948
43949         var cls = 'x-combo-list';
43950
43951         this.list = new Roo.Layer({
43952             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43953         });
43954
43955         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43956         this.list.setWidth(lw);
43957         this.list.swallowEvent('mousewheel');
43958         this.assetHeight = 0;
43959
43960         if(this.title){
43961             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43962             this.assetHeight += this.header.getHeight();
43963         }
43964         this.innerLists = [];
43965         this.views = [];
43966         this.stores = [];
43967         for (var i =0 ; i < this.maxColumns; i++) {
43968             this.onRenderList( cls, i);
43969         }
43970         
43971         // always needs footer, as we are going to have an 'OK' button.
43972         this.footer = this.list.createChild({cls:cls+'-ft'});
43973         this.pageTb = new Roo.Toolbar(this.footer);  
43974         var _this = this;
43975         this.pageTb.add(  {
43976             
43977             text: 'Done',
43978             handler: function()
43979             {
43980                 _this.collapse();
43981             }
43982         });
43983         
43984         if ( this.allowBlank && !this.disableClear) {
43985             
43986             this.pageTb.add(new Roo.Toolbar.Fill(), {
43987                 cls: 'x-btn-icon x-btn-clear',
43988                 text: '&#160;',
43989                 handler: function()
43990                 {
43991                     _this.collapse();
43992                     _this.clearValue();
43993                     _this.onSelect(false, -1);
43994                 }
43995             });
43996         }
43997         if (this.footer) {
43998             this.assetHeight += this.footer.getHeight();
43999         }
44000         
44001     },
44002     onRenderList : function (  cls, i)
44003     {
44004         
44005         var lw = Math.floor(
44006                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44007         );
44008         
44009         this.list.setWidth(lw); // default to '1'
44010
44011         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44012         //il.on('mouseover', this.onViewOver, this, { list:  i });
44013         //il.on('mousemove', this.onViewMove, this, { list:  i });
44014         il.setWidth(lw);
44015         il.setStyle({ 'overflow-x' : 'hidden'});
44016
44017         if(!this.tpl){
44018             this.tpl = new Roo.Template({
44019                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44020                 isEmpty: function (value, allValues) {
44021                     //Roo.log(value);
44022                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44023                     return dl ? 'has-children' : 'no-children'
44024                 }
44025             });
44026         }
44027         
44028         var store  = this.store;
44029         if (i > 0) {
44030             store  = new Roo.data.SimpleStore({
44031                 //fields : this.store.reader.meta.fields,
44032                 reader : this.store.reader,
44033                 data : [ ]
44034             });
44035         }
44036         this.stores[i]  = store;
44037                   
44038         var view = this.views[i] = new Roo.View(
44039             il,
44040             this.tpl,
44041             {
44042                 singleSelect:true,
44043                 store: store,
44044                 selectedClass: this.selectedClass
44045             }
44046         );
44047         view.getEl().setWidth(lw);
44048         view.getEl().setStyle({
44049             position: i < 1 ? 'relative' : 'absolute',
44050             top: 0,
44051             left: (i * lw ) + 'px',
44052             display : i > 0 ? 'none' : 'block'
44053         });
44054         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44055         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44056         //view.on('click', this.onViewClick, this, { list : i });
44057
44058         store.on('beforeload', this.onBeforeLoad, this);
44059         store.on('load',  this.onLoad, this, { list  : i});
44060         store.on('loadexception', this.onLoadException, this);
44061
44062         // hide the other vies..
44063         
44064         
44065         
44066     },
44067       
44068     restrictHeight : function()
44069     {
44070         var mh = 0;
44071         Roo.each(this.innerLists, function(il,i) {
44072             var el = this.views[i].getEl();
44073             el.dom.style.height = '';
44074             var inner = el.dom;
44075             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44076             // only adjust heights on other ones..
44077             mh = Math.max(h, mh);
44078             if (i < 1) {
44079                 
44080                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44081                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44082                
44083             }
44084             
44085             
44086         }, this);
44087         
44088         this.list.beginUpdate();
44089         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44090         this.list.alignTo(this.el, this.listAlign);
44091         this.list.endUpdate();
44092         
44093     },
44094      
44095     
44096     // -- store handlers..
44097     // private
44098     onBeforeLoad : function()
44099     {
44100         if(!this.hasFocus){
44101             return;
44102         }
44103         this.innerLists[0].update(this.loadingText ?
44104                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44105         this.restrictHeight();
44106         this.selectedIndex = -1;
44107     },
44108     // private
44109     onLoad : function(a,b,c,d)
44110     {
44111         if (!this.loadingChildren) {
44112             // then we are loading the top level. - hide the children
44113             for (var i = 1;i < this.views.length; i++) {
44114                 this.views[i].getEl().setStyle({ display : 'none' });
44115             }
44116             var lw = Math.floor(
44117                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44118             );
44119         
44120              this.list.setWidth(lw); // default to '1'
44121
44122             
44123         }
44124         if(!this.hasFocus){
44125             return;
44126         }
44127         
44128         if(this.store.getCount() > 0) {
44129             this.expand();
44130             this.restrictHeight();   
44131         } else {
44132             this.onEmptyResults();
44133         }
44134         
44135         if (!this.loadingChildren) {
44136             this.selectActive();
44137         }
44138         /*
44139         this.stores[1].loadData([]);
44140         this.stores[2].loadData([]);
44141         this.views
44142         */    
44143     
44144         //this.el.focus();
44145     },
44146     
44147     
44148     // private
44149     onLoadException : function()
44150     {
44151         this.collapse();
44152         Roo.log(this.store.reader.jsonData);
44153         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44154             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44155         }
44156         
44157         
44158     },
44159     // no cleaning of leading spaces on blur here.
44160     cleanLeadingSpace : function(e) { },
44161     
44162
44163     onSelectChange : function (view, sels, opts )
44164     {
44165         var ix = view.getSelectedIndexes();
44166          
44167         if (opts.list > this.maxColumns - 2) {
44168             if (view.store.getCount()<  1) {
44169                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44170
44171             } else  {
44172                 if (ix.length) {
44173                     // used to clear ?? but if we are loading unselected 
44174                     this.setFromData(view.store.getAt(ix[0]).data);
44175                 }
44176                 
44177             }
44178             
44179             return;
44180         }
44181         
44182         if (!ix.length) {
44183             // this get's fired when trigger opens..
44184            // this.setFromData({});
44185             var str = this.stores[opts.list+1];
44186             str.data.clear(); // removeall wihtout the fire events..
44187             return;
44188         }
44189         
44190         var rec = view.store.getAt(ix[0]);
44191          
44192         this.setFromData(rec.data);
44193         this.fireEvent('select', this, rec, ix[0]);
44194         
44195         var lw = Math.floor(
44196              (
44197                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44198              ) / this.maxColumns
44199         );
44200         this.loadingChildren = true;
44201         this.stores[opts.list+1].loadDataFromChildren( rec );
44202         this.loadingChildren = false;
44203         var dl = this.stores[opts.list+1]. getTotalCount();
44204         
44205         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44206         
44207         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44208         for (var i = opts.list+2; i < this.views.length;i++) {
44209             this.views[i].getEl().setStyle({ display : 'none' });
44210         }
44211         
44212         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44213         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44214         
44215         if (this.isLoading) {
44216            // this.selectActive(opts.list);
44217         }
44218          
44219     },
44220     
44221     
44222     
44223     
44224     onDoubleClick : function()
44225     {
44226         this.collapse(); //??
44227     },
44228     
44229      
44230     
44231     
44232     
44233     // private
44234     recordToStack : function(store, prop, value, stack)
44235     {
44236         var cstore = new Roo.data.SimpleStore({
44237             //fields : this.store.reader.meta.fields, // we need array reader.. for
44238             reader : this.store.reader,
44239             data : [ ]
44240         });
44241         var _this = this;
44242         var record  = false;
44243         var srec = false;
44244         if(store.getCount() < 1){
44245             return false;
44246         }
44247         store.each(function(r){
44248             if(r.data[prop] == value){
44249                 record = r;
44250             srec = r;
44251                 return false;
44252             }
44253             if (r.data.cn && r.data.cn.length) {
44254                 cstore.loadDataFromChildren( r);
44255                 var cret = _this.recordToStack(cstore, prop, value, stack);
44256                 if (cret !== false) {
44257                     record = cret;
44258                     srec = r;
44259                     return false;
44260                 }
44261             }
44262              
44263             return true;
44264         });
44265         if (record == false) {
44266             return false
44267         }
44268         stack.unshift(srec);
44269         return record;
44270     },
44271     
44272     /*
44273      * find the stack of stores that match our value.
44274      *
44275      * 
44276      */
44277     
44278     selectActive : function ()
44279     {
44280         // if store is not loaded, then we will need to wait for that to happen first.
44281         var stack = [];
44282         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44283         for (var i = 0; i < stack.length; i++ ) {
44284             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44285         }
44286         
44287     }
44288         
44289          
44290     
44291     
44292     
44293     
44294 });/*
44295  * Based on:
44296  * Ext JS Library 1.1.1
44297  * Copyright(c) 2006-2007, Ext JS, LLC.
44298  *
44299  * Originally Released Under LGPL - original licence link has changed is not relivant.
44300  *
44301  * Fork - LGPL
44302  * <script type="text/javascript">
44303  */
44304 /**
44305  * @class Roo.form.Checkbox
44306  * @extends Roo.form.Field
44307  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44308  * @constructor
44309  * Creates a new Checkbox
44310  * @param {Object} config Configuration options
44311  */
44312 Roo.form.Checkbox = function(config){
44313     Roo.form.Checkbox.superclass.constructor.call(this, config);
44314     this.addEvents({
44315         /**
44316          * @event check
44317          * Fires when the checkbox is checked or unchecked.
44318              * @param {Roo.form.Checkbox} this This checkbox
44319              * @param {Boolean} checked The new checked value
44320              */
44321         check : true
44322     });
44323 };
44324
44325 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44326     /**
44327      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44328      */
44329     focusClass : undefined,
44330     /**
44331      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44332      */
44333     fieldClass: "x-form-field",
44334     /**
44335      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44336      */
44337     checked: false,
44338     /**
44339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44340      * {tag: "input", type: "checkbox", autocomplete: "off"})
44341      */
44342     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44343     /**
44344      * @cfg {String} boxLabel The text that appears beside the checkbox
44345      */
44346     boxLabel : "",
44347     /**
44348      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44349      */  
44350     inputValue : '1',
44351     /**
44352      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44353      */
44354      valueOff: '0', // value when not checked..
44355
44356     actionMode : 'viewEl', 
44357     //
44358     // private
44359     itemCls : 'x-menu-check-item x-form-item',
44360     groupClass : 'x-menu-group-item',
44361     inputType : 'hidden',
44362     
44363     
44364     inSetChecked: false, // check that we are not calling self...
44365     
44366     inputElement: false, // real input element?
44367     basedOn: false, // ????
44368     
44369     isFormField: true, // not sure where this is needed!!!!
44370
44371     onResize : function(){
44372         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44373         if(!this.boxLabel){
44374             this.el.alignTo(this.wrap, 'c-c');
44375         }
44376     },
44377
44378     initEvents : function(){
44379         Roo.form.Checkbox.superclass.initEvents.call(this);
44380         this.el.on("click", this.onClick,  this);
44381         this.el.on("change", this.onClick,  this);
44382     },
44383
44384
44385     getResizeEl : function(){
44386         return this.wrap;
44387     },
44388
44389     getPositionEl : function(){
44390         return this.wrap;
44391     },
44392
44393     // private
44394     onRender : function(ct, position){
44395         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44396         /*
44397         if(this.inputValue !== undefined){
44398             this.el.dom.value = this.inputValue;
44399         }
44400         */
44401         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44402         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44403         var viewEl = this.wrap.createChild({ 
44404             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44405         this.viewEl = viewEl;   
44406         this.wrap.on('click', this.onClick,  this); 
44407         
44408         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44409         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44410         
44411         
44412         
44413         if(this.boxLabel){
44414             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44415         //    viewEl.on('click', this.onClick,  this); 
44416         }
44417         //if(this.checked){
44418             this.setChecked(this.checked);
44419         //}else{
44420             //this.checked = this.el.dom;
44421         //}
44422
44423     },
44424
44425     // private
44426     initValue : Roo.emptyFn,
44427
44428     /**
44429      * Returns the checked state of the checkbox.
44430      * @return {Boolean} True if checked, else false
44431      */
44432     getValue : function(){
44433         if(this.el){
44434             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44435         }
44436         return this.valueOff;
44437         
44438     },
44439
44440         // private
44441     onClick : function(){ 
44442         if (this.disabled) {
44443             return;
44444         }
44445         this.setChecked(!this.checked);
44446
44447         //if(this.el.dom.checked != this.checked){
44448         //    this.setValue(this.el.dom.checked);
44449        // }
44450     },
44451
44452     /**
44453      * Sets the checked state of the checkbox.
44454      * On is always based on a string comparison between inputValue and the param.
44455      * @param {Boolean/String} value - the value to set 
44456      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44457      */
44458     setValue : function(v,suppressEvent){
44459         
44460         
44461         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44462         //if(this.el && this.el.dom){
44463         //    this.el.dom.checked = this.checked;
44464         //    this.el.dom.defaultChecked = this.checked;
44465         //}
44466         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44467         //this.fireEvent("check", this, this.checked);
44468     },
44469     // private..
44470     setChecked : function(state,suppressEvent)
44471     {
44472         if (this.inSetChecked) {
44473             this.checked = state;
44474             return;
44475         }
44476         
44477     
44478         if(this.wrap){
44479             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44480         }
44481         this.checked = state;
44482         if(suppressEvent !== true){
44483             this.fireEvent('check', this, state);
44484         }
44485         this.inSetChecked = true;
44486         this.el.dom.value = state ? this.inputValue : this.valueOff;
44487         this.inSetChecked = false;
44488         
44489     },
44490     // handle setting of hidden value by some other method!!?!?
44491     setFromHidden: function()
44492     {
44493         if(!this.el){
44494             return;
44495         }
44496         //console.log("SET FROM HIDDEN");
44497         //alert('setFrom hidden');
44498         this.setValue(this.el.dom.value);
44499     },
44500     
44501     onDestroy : function()
44502     {
44503         if(this.viewEl){
44504             Roo.get(this.viewEl).remove();
44505         }
44506          
44507         Roo.form.Checkbox.superclass.onDestroy.call(this);
44508     },
44509     
44510     setBoxLabel : function(str)
44511     {
44512         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44513     }
44514
44515 });/*
44516  * Based on:
44517  * Ext JS Library 1.1.1
44518  * Copyright(c) 2006-2007, Ext JS, LLC.
44519  *
44520  * Originally Released Under LGPL - original licence link has changed is not relivant.
44521  *
44522  * Fork - LGPL
44523  * <script type="text/javascript">
44524  */
44525  
44526 /**
44527  * @class Roo.form.Radio
44528  * @extends Roo.form.Checkbox
44529  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44530  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44531  * @constructor
44532  * Creates a new Radio
44533  * @param {Object} config Configuration options
44534  */
44535 Roo.form.Radio = function(){
44536     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44537 };
44538 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44539     inputType: 'radio',
44540
44541     /**
44542      * If this radio is part of a group, it will return the selected value
44543      * @return {String}
44544      */
44545     getGroupValue : function(){
44546         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44547     },
44548     
44549     
44550     onRender : function(ct, position){
44551         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44552         
44553         if(this.inputValue !== undefined){
44554             this.el.dom.value = this.inputValue;
44555         }
44556          
44557         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44558         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44559         //var viewEl = this.wrap.createChild({ 
44560         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44561         //this.viewEl = viewEl;   
44562         //this.wrap.on('click', this.onClick,  this); 
44563         
44564         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44565         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44566         
44567         
44568         
44569         if(this.boxLabel){
44570             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44571         //    viewEl.on('click', this.onClick,  this); 
44572         }
44573          if(this.checked){
44574             this.el.dom.checked =   'checked' ;
44575         }
44576          
44577     } 
44578     
44579     
44580 });Roo.rtf = {}; // namespace
44581 Roo.rtf.Hex = function(hex)
44582 {
44583     this.hexstr = hex;
44584 };
44585 Roo.rtf.Paragraph = function(opts)
44586 {
44587     this.content = []; ///??? is that used?
44588 };Roo.rtf.Span = function(opts)
44589 {
44590     this.value = opts.value;
44591 };
44592
44593 Roo.rtf.Group = function(parent)
44594 {
44595     // we dont want to acutally store parent - it will make debug a nightmare..
44596     this.content = [];
44597     this.cn  = [];
44598      
44599        
44600     
44601 };
44602
44603 Roo.rtf.Group.prototype = {
44604     ignorable : false,
44605     content: false,
44606     cn: false,
44607     addContent : function(node) {
44608         // could set styles...
44609         this.content.push(node);
44610     },
44611     addChild : function(cn)
44612     {
44613         this.cn.push(cn);
44614     },
44615     // only for images really...
44616     toDataURL : function()
44617     {
44618         var mimetype = false;
44619         switch(true) {
44620             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44621                 mimetype = "image/png";
44622                 break;
44623              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44624                 mimetype = "image/jpeg";
44625                 break;
44626             default :
44627                 return 'about:blank'; // ?? error?
44628         }
44629         
44630         
44631         var hexstring = this.content[this.content.length-1].value;
44632         
44633         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44634             return String.fromCharCode(parseInt(a, 16));
44635         }).join(""));
44636     }
44637     
44638 };
44639 // this looks like it's normally the {rtf{ .... }}
44640 Roo.rtf.Document = function()
44641 {
44642     // we dont want to acutally store parent - it will make debug a nightmare..
44643     this.rtlch  = [];
44644     this.content = [];
44645     this.cn = [];
44646     
44647 };
44648 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44649     addChild : function(cn)
44650     {
44651         this.cn.push(cn);
44652         switch(cn.type) {
44653             case 'rtlch': // most content seems to be inside this??
44654             case 'listtext':
44655             case 'shpinst':
44656                 this.rtlch.push(cn);
44657                 return;
44658             default:
44659                 this[cn.type] = cn;
44660         }
44661         
44662     },
44663     
44664     getElementsByType : function(type)
44665     {
44666         var ret =  [];
44667         this._getElementsByType(type, ret, this.cn, 'rtf');
44668         return ret;
44669     },
44670     _getElementsByType : function (type, ret, search_array, path)
44671     {
44672         search_array.forEach(function(n,i) {
44673             if (n.type == type) {
44674                 n.path = path + '/' + n.type + ':' + i;
44675                 ret.push(n);
44676             }
44677             if (n.cn.length > 0) {
44678                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44679             }
44680         },this);
44681     }
44682     
44683 });
44684  
44685 Roo.rtf.Ctrl = function(opts)
44686 {
44687     this.value = opts.value;
44688     this.param = opts.param;
44689 };
44690 /**
44691  *
44692  *
44693  * based on this https://github.com/iarna/rtf-parser
44694  * it's really only designed to extract pict from pasted RTF 
44695  *
44696  * usage:
44697  *
44698  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44699  *  
44700  *
44701  */
44702
44703  
44704
44705
44706
44707 Roo.rtf.Parser = function(text) {
44708     //super({objectMode: true})
44709     this.text = '';
44710     this.parserState = this.parseText;
44711     
44712     // these are for interpeter...
44713     this.doc = {};
44714     ///this.parserState = this.parseTop
44715     this.groupStack = [];
44716     this.hexStore = [];
44717     this.doc = false;
44718     
44719     this.groups = []; // where we put the return.
44720     
44721     for (var ii = 0; ii < text.length; ++ii) {
44722         ++this.cpos;
44723         
44724         if (text[ii] === '\n') {
44725             ++this.row;
44726             this.col = 1;
44727         } else {
44728             ++this.col;
44729         }
44730         this.parserState(text[ii]);
44731     }
44732     
44733     
44734     
44735 };
44736 Roo.rtf.Parser.prototype = {
44737     text : '', // string being parsed..
44738     controlWord : '',
44739     controlWordParam :  '',
44740     hexChar : '',
44741     doc : false,
44742     group: false,
44743     groupStack : false,
44744     hexStore : false,
44745     
44746     
44747     cpos : 0, 
44748     row : 1, // reportin?
44749     col : 1, //
44750
44751      
44752     push : function (el)
44753     {
44754         var m = 'cmd'+ el.type;
44755         if (typeof(this[m]) == 'undefined') {
44756             Roo.log('invalid cmd:' + el.type);
44757             return;
44758         }
44759         this[m](el);
44760         //Roo.log(el);
44761     },
44762     flushHexStore : function()
44763     {
44764         if (this.hexStore.length < 1) {
44765             return;
44766         }
44767         var hexstr = this.hexStore.map(
44768             function(cmd) {
44769                 return cmd.value;
44770         }).join('');
44771         
44772         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44773               
44774             
44775         this.hexStore.splice(0)
44776         
44777     },
44778     
44779     cmdgroupstart : function()
44780     {
44781         this.flushHexStore();
44782         if (this.group) {
44783             this.groupStack.push(this.group);
44784         }
44785          // parent..
44786         if (this.doc === false) {
44787             this.group = this.doc = new Roo.rtf.Document();
44788             return;
44789             
44790         }
44791         this.group = new Roo.rtf.Group(this.group);
44792     },
44793     cmdignorable : function()
44794     {
44795         this.flushHexStore();
44796         this.group.ignorable = true;
44797     },
44798     cmdendparagraph : function()
44799     {
44800         this.flushHexStore();
44801         this.group.addContent(new Roo.rtf.Paragraph());
44802     },
44803     cmdgroupend : function ()
44804     {
44805         this.flushHexStore();
44806         var endingGroup = this.group;
44807         
44808         
44809         this.group = this.groupStack.pop();
44810         if (this.group) {
44811             this.group.addChild(endingGroup);
44812         }
44813         
44814         
44815         
44816         var doc = this.group || this.doc;
44817         //if (endingGroup instanceof FontTable) {
44818         //  doc.fonts = endingGroup.table
44819         //} else if (endingGroup instanceof ColorTable) {
44820         //  doc.colors = endingGroup.table
44821         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44822         if (endingGroup.ignorable === false) {
44823             //code
44824             this.groups.push(endingGroup);
44825            // Roo.log( endingGroup );
44826         }
44827             //Roo.each(endingGroup.content, function(item)) {
44828             //    doc.addContent(item);
44829             //}
44830             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44831         //}
44832     },
44833     cmdtext : function (cmd)
44834     {
44835         this.flushHexStore();
44836         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44837             //this.group = this.doc
44838         }
44839         this.group.addContent(new Roo.rtf.Span(cmd));
44840     },
44841     cmdcontrolword : function (cmd)
44842     {
44843         this.flushHexStore();
44844         if (!this.group.type) {
44845             this.group.type = cmd.value;
44846             return;
44847         }
44848         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44849         // we actually don't care about ctrl words...
44850         return ;
44851         /*
44852         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44853         if (this[method]) {
44854             this[method](cmd.param)
44855         } else {
44856             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44857         }
44858         */
44859     },
44860     cmdhexchar : function(cmd) {
44861         this.hexStore.push(cmd);
44862     },
44863     cmderror : function(cmd) {
44864         throw new Exception (cmd.value);
44865     },
44866     
44867     /*
44868       _flush (done) {
44869         if (this.text !== '\u0000') this.emitText()
44870         done()
44871       }
44872       */
44873       
44874       
44875     parseText : function(c)
44876     {
44877         if (c === '\\') {
44878             this.parserState = this.parseEscapes;
44879         } else if (c === '{') {
44880             this.emitStartGroup();
44881         } else if (c === '}') {
44882             this.emitEndGroup();
44883         } else if (c === '\x0A' || c === '\x0D') {
44884             // cr/lf are noise chars
44885         } else {
44886             this.text += c;
44887         }
44888     },
44889     
44890     parseEscapes: function (c)
44891     {
44892         if (c === '\\' || c === '{' || c === '}') {
44893             this.text += c;
44894             this.parserState = this.parseText;
44895         } else {
44896             this.parserState = this.parseControlSymbol;
44897             this.parseControlSymbol(c);
44898         }
44899     },
44900     parseControlSymbol: function(c)
44901     {
44902         if (c === '~') {
44903             this.text += '\u00a0'; // nbsp
44904             this.parserState = this.parseText
44905         } else if (c === '-') {
44906              this.text += '\u00ad'; // soft hyphen
44907         } else if (c === '_') {
44908             this.text += '\u2011'; // non-breaking hyphen
44909         } else if (c === '*') {
44910             this.emitIgnorable();
44911             this.parserState = this.parseText;
44912         } else if (c === "'") {
44913             this.parserState = this.parseHexChar;
44914         } else if (c === '|') { // formula cacter
44915             this.emitFormula();
44916             this.parserState = this.parseText;
44917         } else if (c === ':') { // subentry in an index entry
44918             this.emitIndexSubEntry();
44919             this.parserState = this.parseText;
44920         } else if (c === '\x0a') {
44921             this.emitEndParagraph();
44922             this.parserState = this.parseText;
44923         } else if (c === '\x0d') {
44924             this.emitEndParagraph();
44925             this.parserState = this.parseText;
44926         } else {
44927             this.parserState = this.parseControlWord;
44928             this.parseControlWord(c);
44929         }
44930     },
44931     parseHexChar: function (c)
44932     {
44933         if (/^[A-Fa-f0-9]$/.test(c)) {
44934             this.hexChar += c;
44935             if (this.hexChar.length >= 2) {
44936               this.emitHexChar();
44937               this.parserState = this.parseText;
44938             }
44939             return;
44940         }
44941         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44942         this.parserState = this.parseText;
44943         
44944     },
44945     parseControlWord : function(c)
44946     {
44947         if (c === ' ') {
44948             this.emitControlWord();
44949             this.parserState = this.parseText;
44950         } else if (/^[-\d]$/.test(c)) {
44951             this.parserState = this.parseControlWordParam;
44952             this.controlWordParam += c;
44953         } else if (/^[A-Za-z]$/.test(c)) {
44954           this.controlWord += c;
44955         } else {
44956           this.emitControlWord();
44957           this.parserState = this.parseText;
44958           this.parseText(c);
44959         }
44960     },
44961     parseControlWordParam : function (c) {
44962         if (/^\d$/.test(c)) {
44963           this.controlWordParam += c;
44964         } else if (c === ' ') {
44965           this.emitControlWord();
44966           this.parserState = this.parseText;
44967         } else {
44968           this.emitControlWord();
44969           this.parserState = this.parseText;
44970           this.parseText(c);
44971         }
44972     },
44973     
44974     
44975     
44976     
44977     emitText : function () {
44978         if (this.text === '') {
44979             return;
44980         }
44981         this.push({
44982             type: 'text',
44983             value: this.text,
44984             pos: this.cpos,
44985             row: this.row,
44986             col: this.col
44987         });
44988         this.text = ''
44989     },
44990     emitControlWord : function ()
44991     {
44992         this.emitText();
44993         if (this.controlWord === '') {
44994             this.emitError('empty control word');
44995         } else {
44996             this.push({
44997                   type: 'controlword',
44998                   value: this.controlWord,
44999                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45000                   pos: this.cpos,
45001                   row: this.row,
45002                   col: this.col
45003             });
45004         }
45005         this.controlWord = '';
45006         this.controlWordParam = '';
45007     },
45008     emitStartGroup : function ()
45009     {
45010         this.emitText();
45011         this.push({
45012             type: 'groupstart',
45013             pos: this.cpos,
45014             row: this.row,
45015             col: this.col
45016         });
45017     },
45018     emitEndGroup : function ()
45019     {
45020         this.emitText();
45021         this.push({
45022             type: 'groupend',
45023             pos: this.cpos,
45024             row: this.row,
45025             col: this.col
45026         });
45027     },
45028     emitIgnorable : function ()
45029     {
45030         this.emitText();
45031         this.push({
45032             type: 'ignorable',
45033             pos: this.cpos,
45034             row: this.row,
45035             col: this.col
45036         });
45037     },
45038     emitHexChar : function ()
45039     {
45040         this.emitText();
45041         this.push({
45042             type: 'hexchar',
45043             value: this.hexChar,
45044             pos: this.cpos,
45045             row: this.row,
45046             col: this.col
45047         });
45048         this.hexChar = ''
45049     },
45050     emitError : function (message)
45051     {
45052       this.emitText();
45053       this.push({
45054             type: 'error',
45055             value: message,
45056             row: this.row,
45057             col: this.col,
45058             char: this.cpos //,
45059             //stack: new Error().stack
45060         });
45061     },
45062     emitEndParagraph : function () {
45063         this.emitText();
45064         this.push({
45065             type: 'endparagraph',
45066             pos: this.cpos,
45067             row: this.row,
45068             col: this.col
45069         });
45070     }
45071      
45072 } ;
45073 Roo.htmleditor = {};
45074  
45075 /**
45076  * @class Roo.htmleditor.Filter
45077  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45078  * @cfg {DomElement} node The node to iterate and filter
45079  * @cfg {boolean|String|Array} tag Tags to replace 
45080  * @constructor
45081  * Create a new Filter.
45082  * @param {Object} config Configuration options
45083  */
45084
45085
45086
45087 Roo.htmleditor.Filter = function(cfg) {
45088     Roo.apply(this.cfg);
45089     // this does not actually call walk as it's really just a abstract class
45090 }
45091
45092
45093 Roo.htmleditor.Filter.prototype = {
45094     
45095     node: false,
45096     
45097     tag: false,
45098
45099     // overrride to do replace comments.
45100     replaceComment : false,
45101     
45102     // overrride to do replace or do stuff with tags..
45103     replaceTag : false,
45104     
45105     walk : function(dom)
45106     {
45107         Roo.each( Array.from(dom.childNodes), function( e ) {
45108             switch(true) {
45109                 
45110                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45111                     this.replaceComment(e);
45112                     return;
45113                 
45114                 case e.nodeType != 1: //not a node.
45115                     return;
45116                 
45117                 case this.tag === true: // everything
45118                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45119                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45120                     if (this.replaceTag && false === this.replaceTag(e)) {
45121                         return;
45122                     }
45123                     if (e.hasChildNodes()) {
45124                         this.walk(e);
45125                     }
45126                     return;
45127                 
45128                 default:    // tags .. that do not match.
45129                     if (e.hasChildNodes()) {
45130                         this.walk(e);
45131                     }
45132             }
45133             
45134         }, this);
45135         
45136     }
45137 }; 
45138
45139 /**
45140  * @class Roo.htmleditor.FilterAttributes
45141  * clean attributes and  styles including http:// etc.. in attribute
45142  * @constructor
45143 * Run a new Attribute Filter
45144 * @param {Object} config Configuration options
45145  */
45146 Roo.htmleditor.FilterAttributes = function(cfg)
45147 {
45148     Roo.apply(this, cfg);
45149     this.attrib_black = this.attrib_black || [];
45150     this.attrib_white = this.attrib_white || [];
45151
45152     this.attrib_clean = this.attrib_clean || [];
45153     this.style_white = this.style_white || [];
45154     this.style_black = this.style_black || [];
45155     this.walk(cfg.node);
45156 }
45157
45158 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45159 {
45160     tag: true, // all tags
45161     
45162     attrib_black : false, // array
45163     attrib_clean : false,
45164     attrib_white : false,
45165
45166     style_white : false,
45167     style_black : false,
45168      
45169      
45170     replaceTag : function(node)
45171     {
45172         if (!node.attributes || !node.attributes.length) {
45173             return true;
45174         }
45175         
45176         for (var i = node.attributes.length-1; i > -1 ; i--) {
45177             var a = node.attributes[i];
45178             //console.log(a);
45179             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45180                 node.removeAttribute(a.name);
45181                 continue;
45182             }
45183             
45184             
45185             
45186             if (a.name.toLowerCase().substr(0,2)=='on')  {
45187                 node.removeAttribute(a.name);
45188                 continue;
45189             }
45190             
45191             
45192             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45193                 node.removeAttribute(a.name);
45194                 continue;
45195             }
45196             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45197                 this.cleanAttr(node,a.name,a.value); // fixme..
45198                 continue;
45199             }
45200             if (a.name == 'style') {
45201                 this.cleanStyle(node,a.name,a.value);
45202                 continue;
45203             }
45204             /// clean up MS crap..
45205             // tecnically this should be a list of valid class'es..
45206             
45207             
45208             if (a.name == 'class') {
45209                 if (a.value.match(/^Mso/)) {
45210                     node.removeAttribute('class');
45211                 }
45212                 
45213                 if (a.value.match(/^body$/)) {
45214                     node.removeAttribute('class');
45215                 }
45216                 continue;
45217             }
45218             
45219             
45220             // style cleanup!?
45221             // class cleanup?
45222             
45223         }
45224         return true; // clean children
45225     },
45226         
45227     cleanAttr: function(node, n,v)
45228     {
45229         
45230         if (v.match(/^\./) || v.match(/^\//)) {
45231             return;
45232         }
45233         if (v.match(/^(http|https):\/\//)
45234             || v.match(/^mailto:/) 
45235             || v.match(/^ftp:/)
45236             || v.match(/^data:/)
45237             ) {
45238             return;
45239         }
45240         if (v.match(/^#/)) {
45241             return;
45242         }
45243         if (v.match(/^\{/)) { // allow template editing.
45244             return;
45245         }
45246 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45247         node.removeAttribute(n);
45248         
45249     },
45250     cleanStyle : function(node,  n,v)
45251     {
45252         if (v.match(/expression/)) { //XSS?? should we even bother..
45253             node.removeAttribute(n);
45254             return;
45255         }
45256         
45257         var parts = v.split(/;/);
45258         var clean = [];
45259         
45260         Roo.each(parts, function(p) {
45261             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45262             if (!p.length) {
45263                 return true;
45264             }
45265             var l = p.split(':').shift().replace(/\s+/g,'');
45266             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45267             
45268             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45269                 return true;
45270             }
45271             //Roo.log()
45272             // only allow 'c whitelisted system attributes'
45273             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45274                 return true;
45275             }
45276             
45277             
45278             clean.push(p);
45279             return true;
45280         },this);
45281         if (clean.length) { 
45282             node.setAttribute(n, clean.join(';'));
45283         } else {
45284             node.removeAttribute(n);
45285         }
45286         
45287     }
45288         
45289         
45290         
45291     
45292 });/**
45293  * @class Roo.htmleditor.FilterBlack
45294  * remove blacklisted elements.
45295  * @constructor
45296  * Run a new Blacklisted Filter
45297  * @param {Object} config Configuration options
45298  */
45299
45300 Roo.htmleditor.FilterBlack = function(cfg)
45301 {
45302     Roo.apply(this, cfg);
45303     this.walk(cfg.node);
45304 }
45305
45306 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45307 {
45308     tag : true, // all elements.
45309    
45310     replace : function(n)
45311     {
45312         n.parentNode.removeChild(n);
45313     }
45314 });
45315 /**
45316  * @class Roo.htmleditor.FilterComment
45317  * remove comments.
45318  * @constructor
45319 * Run a new Comments Filter
45320 * @param {Object} config Configuration options
45321  */
45322 Roo.htmleditor.FilterComment = function(cfg)
45323 {
45324     this.walk(cfg.node);
45325 }
45326
45327 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45328 {
45329   
45330     replaceComment : function(n)
45331     {
45332         n.parentNode.removeChild(n);
45333     }
45334 });/**
45335  * @class Roo.htmleditor.FilterKeepChildren
45336  * remove tags but keep children
45337  * @constructor
45338  * Run a new Keep Children Filter
45339  * @param {Object} config Configuration options
45340  */
45341
45342 Roo.htmleditor.FilterKeepChildren = function(cfg)
45343 {
45344     Roo.apply(this, cfg);
45345     if (this.tag === false) {
45346         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45347     }
45348     this.walk(cfg.node);
45349 }
45350
45351 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45352 {
45353     
45354   
45355     replaceTag : function(node)
45356     {
45357         // walk children...
45358         //Roo.log(node);
45359         var ar = Array.from(node.childNodes);
45360         //remove first..
45361         for (var i = 0; i < ar.length; i++) {
45362             if (ar[i].nodeType == 1) {
45363                 if (
45364                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45365                     || // array and it matches
45366                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45367                 ) {
45368                     this.replaceTag(ar[i]); // child is blacklisted as well...
45369                     continue;
45370                 }
45371             }
45372         }  
45373         ar = Array.from(node.childNodes);
45374         for (var i = 0; i < ar.length; i++) {
45375          
45376             node.removeChild(ar[i]);
45377             // what if we need to walk these???
45378             node.parentNode.insertBefore(ar[i], node);
45379             if (this.tag !== false) {
45380                 this.walk(ar[i]);
45381                 
45382             }
45383         }
45384         node.parentNode.removeChild(node);
45385         return false; // don't walk children
45386         
45387         
45388     }
45389 });/**
45390  * @class Roo.htmleditor.FilterParagraph
45391  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45392  * like on 'push' to remove the <p> tags and replace them with line breaks.
45393  * @constructor
45394  * Run a new Paragraph Filter
45395  * @param {Object} config Configuration options
45396  */
45397
45398 Roo.htmleditor.FilterParagraph = function(cfg)
45399 {
45400     // no need to apply config.
45401     this.walk(cfg.node);
45402 }
45403
45404 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45405 {
45406     
45407      
45408     tag : 'P',
45409     
45410      
45411     replaceTag : function(node)
45412     {
45413         
45414         if (node.childNodes.length == 1 &&
45415             node.childNodes[0].nodeType == 3 &&
45416             node.childNodes[0].textContent.trim().length < 1
45417             ) {
45418             // remove and replace with '<BR>';
45419             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45420             return false; // no need to walk..
45421         }
45422         var ar = Array.from(node.childNodes);
45423         for (var i = 0; i < ar.length; i++) {
45424             node.removeChild(ar[i]);
45425             // what if we need to walk these???
45426             node.parentNode.insertBefore(ar[i], node);
45427         }
45428         // now what about this?
45429         // <p> &nbsp; </p>
45430         
45431         // double BR.
45432         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45433         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45434         node.parentNode.removeChild(node);
45435         
45436         return false;
45437
45438     }
45439     
45440 });/**
45441  * @class Roo.htmleditor.FilterSpan
45442  * filter span's with no attributes out..
45443  * @constructor
45444  * Run a new Span Filter
45445  * @param {Object} config Configuration options
45446  */
45447
45448 Roo.htmleditor.FilterSpan = function(cfg)
45449 {
45450     // no need to apply config.
45451     this.walk(cfg.node);
45452 }
45453
45454 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45455 {
45456      
45457     tag : 'SPAN',
45458      
45459  
45460     replaceTag : function(node)
45461     {
45462         if (node.attributes && node.attributes.length > 0) {
45463             return true; // walk if there are any.
45464         }
45465         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45466         return false;
45467      
45468     }
45469     
45470 });/**
45471  * @class Roo.htmleditor.FilterTableWidth
45472   try and remove table width data - as that frequently messes up other stuff.
45473  * 
45474  *      was cleanTableWidths.
45475  *
45476  * Quite often pasting from word etc.. results in tables with column and widths.
45477  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45478  *
45479  * @constructor
45480  * Run a new Table Filter
45481  * @param {Object} config Configuration options
45482  */
45483
45484 Roo.htmleditor.FilterTableWidth = function(cfg)
45485 {
45486     // no need to apply config.
45487     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45488     this.walk(cfg.node);
45489 }
45490
45491 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45492 {
45493      
45494      
45495     
45496     replaceTag: function(node) {
45497         
45498         
45499       
45500         if (node.hasAttribute('width')) {
45501             node.removeAttribute('width');
45502         }
45503         
45504          
45505         if (node.hasAttribute("style")) {
45506             // pretty basic...
45507             
45508             var styles = node.getAttribute("style").split(";");
45509             var nstyle = [];
45510             Roo.each(styles, function(s) {
45511                 if (!s.match(/:/)) {
45512                     return;
45513                 }
45514                 var kv = s.split(":");
45515                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45516                     return;
45517                 }
45518                 // what ever is left... we allow.
45519                 nstyle.push(s);
45520             });
45521             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45522             if (!nstyle.length) {
45523                 node.removeAttribute('style');
45524             }
45525         }
45526         
45527         return true; // continue doing children..
45528     }
45529 });/**
45530  * @class Roo.htmleditor.FilterWord
45531  * try and clean up all the mess that Word generates.
45532  * 
45533  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45534  
45535  * @constructor
45536  * Run a new Span Filter
45537  * @param {Object} config Configuration options
45538  */
45539
45540 Roo.htmleditor.FilterWord = function(cfg)
45541 {
45542     // no need to apply config.
45543     this.walk(cfg.node);
45544 }
45545
45546 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45547 {
45548     tag: true,
45549      
45550     
45551     /**
45552      * Clean up MS wordisms...
45553      */
45554     replaceTag : function(node)
45555     {
45556          
45557         // no idea what this does - span with text, replaceds with just text.
45558         if(
45559                 node.nodeName == 'SPAN' &&
45560                 !node.hasAttributes() &&
45561                 node.childNodes.length == 1 &&
45562                 node.firstChild.nodeName == "#text"  
45563         ) {
45564             var textNode = node.firstChild;
45565             node.removeChild(textNode);
45566             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45567                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45568             }
45569             node.parentNode.insertBefore(textNode, node);
45570             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45571                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45572             }
45573             
45574             node.parentNode.removeChild(node);
45575             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45576         }
45577         
45578    
45579         
45580         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45581             node.parentNode.removeChild(node);
45582             return false; // dont do chidlren
45583         }
45584         //Roo.log(node.tagName);
45585         // remove - but keep children..
45586         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45587             //Roo.log('-- removed');
45588             while (node.childNodes.length) {
45589                 var cn = node.childNodes[0];
45590                 node.removeChild(cn);
45591                 node.parentNode.insertBefore(cn, node);
45592                 // move node to parent - and clean it..
45593                 this.replaceTag(cn);
45594             }
45595             node.parentNode.removeChild(node);
45596             /// no need to iterate chidlren = it's got none..
45597             //this.iterateChildren(node, this.cleanWord);
45598             return false; // no need to iterate children.
45599         }
45600         // clean styles
45601         if (node.className.length) {
45602             
45603             var cn = node.className.split(/\W+/);
45604             var cna = [];
45605             Roo.each(cn, function(cls) {
45606                 if (cls.match(/Mso[a-zA-Z]+/)) {
45607                     return;
45608                 }
45609                 cna.push(cls);
45610             });
45611             node.className = cna.length ? cna.join(' ') : '';
45612             if (!cna.length) {
45613                 node.removeAttribute("class");
45614             }
45615         }
45616         
45617         if (node.hasAttribute("lang")) {
45618             node.removeAttribute("lang");
45619         }
45620         
45621         if (node.hasAttribute("style")) {
45622             
45623             var styles = node.getAttribute("style").split(";");
45624             var nstyle = [];
45625             Roo.each(styles, function(s) {
45626                 if (!s.match(/:/)) {
45627                     return;
45628                 }
45629                 var kv = s.split(":");
45630                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45631                     return;
45632                 }
45633                 // what ever is left... we allow.
45634                 nstyle.push(s);
45635             });
45636             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45637             if (!nstyle.length) {
45638                 node.removeAttribute('style');
45639             }
45640         }
45641         return true; // do children
45642         
45643         
45644         
45645     }
45646 });
45647 /**
45648  * @class Roo.htmleditor.FilterStyleToTag
45649  * part of the word stuff... - certain 'styles' should be converted to tags.
45650  * eg.
45651  *   font-weight: bold -> bold
45652  *   ?? super / subscrit etc..
45653  * 
45654  * @constructor
45655 * Run a new style to tag filter.
45656 * @param {Object} config Configuration options
45657  */
45658 Roo.htmleditor.FilterStyleToTag = function(cfg)
45659 {
45660     
45661     this.tags = {
45662         B  : [ 'fontWeight' , 'bold'],
45663         I :  [ 'fontStyle' , 'italic'],
45664         //pre :  [ 'font-style' , 'italic'],
45665         // h1.. h6 ?? font-size?
45666         SUP : [ 'verticalAlign' , 'super' ],
45667         SUB : [ 'verticalAlign' , 'sub' ]
45668         
45669         
45670     };
45671     
45672     Roo.apply(this, cfg);
45673      
45674     
45675     this.walk(cfg.node);
45676     
45677     
45678     
45679 }
45680
45681
45682 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45683 {
45684     tag: true, // all tags
45685     
45686     tags : false,
45687     
45688     
45689     replaceTag : function(node)
45690     {
45691         
45692         
45693         if (node.getAttribute("style") === null) {
45694             return true;
45695         }
45696         var inject = [];
45697         for (var k in this.tags) {
45698             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45699                 inject.push(k);
45700                 node.style.removeProperty(this.tags[k][0]);
45701             }
45702         }
45703         if (!inject.length) {
45704             return true; 
45705         }
45706         var cn = Array.from(node.childNodes);
45707         var nn = node;
45708         Roo.each(inject, function(t) {
45709             var nc = node.ownerDocument.createelement(t);
45710             nn.appendChild(nc);
45711             nn = nc;
45712         });
45713         for(var i = 0;i < cn.length;cn++) {
45714             node.removeChild(cn[i]);
45715             nn.appendChild(cn[i]);
45716         }
45717         return true /// iterate thru
45718     }
45719     
45720 })/**
45721  * @class Roo.htmleditor.FilterLongBr
45722  * BR/BR/BR - keep a maximum of 2...
45723  * @constructor
45724  * Run a new Long BR Filter
45725  * @param {Object} config Configuration options
45726  */
45727
45728 Roo.htmleditor.FilterLongBr = function(cfg)
45729 {
45730     // no need to apply config.
45731     this.walk(cfg.node);
45732 }
45733
45734 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45735 {
45736     
45737      
45738     tag : 'BR',
45739     
45740      
45741     replaceTag : function(node)
45742     {
45743         
45744         var ps = node.nextSibling;
45745         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45746             ps = ps.nextSibling;
45747         }
45748         
45749         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45750             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45751             return false;
45752         }
45753         
45754         if (!ps || ps.nodeType != 1) {
45755             return false;
45756         }
45757         
45758         if (!ps || ps.tagName != 'BR') {
45759            
45760             return false;
45761         }
45762         
45763         
45764         
45765         
45766         
45767         if (!node.previousSibling) {
45768             return false;
45769         }
45770         var ps = node.previousSibling;
45771         
45772         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45773             ps = ps.previousSibling;
45774         }
45775         if (!ps || ps.nodeType != 1) {
45776             return false;
45777         }
45778         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45779         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45780             return false;
45781         }
45782         
45783         node.parentNode.removeChild(node); // remove me...
45784         
45785         return false; // no need to do children
45786
45787     }
45788     
45789 });
45790 /**
45791  * @class Roo.htmleditor.Tidy
45792  * Tidy HTML 
45793  * @cfg {Roo.HtmlEditorCore} core the editor.
45794  * @constructor
45795  * Create a new Filter.
45796  * @param {Object} config Configuration options
45797  */
45798
45799
45800 Roo.htmleditor.Tidy = function(cfg) {
45801     Roo.apply(this, cfg);
45802     
45803     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45804      
45805 }
45806
45807 Roo.htmleditor.Tidy.toString = function(node)
45808 {
45809     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45810 }
45811
45812 Roo.htmleditor.Tidy.prototype = {
45813     
45814     
45815     wrap : function(s) {
45816         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45817     },
45818
45819     
45820     tidy : function(node, indent) {
45821      
45822         if  (node.nodeType == 3) {
45823             // text.
45824             
45825             
45826             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45827                 
45828             
45829         }
45830         
45831         if  (node.nodeType != 1) {
45832             return '';
45833         }
45834         
45835         
45836         
45837         if (node.tagName == 'BODY') {
45838             
45839             return this.cn(node, '');
45840         }
45841              
45842              // Prints the node tagName, such as <A>, <IMG>, etc
45843         var ret = "<" + node.tagName +  this.attr(node) ;
45844         
45845         // elements with no children..
45846         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45847                 return ret + '/>';
45848         }
45849         ret += '>';
45850         
45851         
45852         var cindent = indent === false ? '' : (indent + '  ');
45853         // tags where we will not pad the children.. (inline text tags etc..)
45854         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45855             cindent = false;
45856             
45857             
45858         }
45859         
45860         var cn = this.cn(node, cindent );
45861         
45862         return ret + cn  + '</' + node.tagName + '>';
45863         
45864     },
45865     cn: function(node, indent)
45866     {
45867         var ret = [];
45868         
45869         var ar = Array.from(node.childNodes);
45870         for (var i = 0 ; i < ar.length ; i++) {
45871             
45872             
45873             
45874             if (indent !== false   // indent==false preservies everything
45875                 && i > 0
45876                 && ar[i].nodeType == 3 
45877                 && ar[i].nodeValue.length > 0
45878                 && ar[i].nodeValue.match(/^\s+/)
45879             ) {
45880                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45881                     ret.pop(); // remove line break from last?
45882                 }
45883                 
45884                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45885             }
45886             if (indent !== false
45887                 && ar[i].nodeType == 1 // element - and indent is not set... 
45888             ) {
45889                 ret.push("\n" + indent); 
45890             }
45891             
45892             ret.push(this.tidy(ar[i], indent));
45893             // text + trailing indent 
45894             if (indent !== false
45895                 && ar[i].nodeType == 3
45896                 && ar[i].nodeValue.length > 0
45897                 && ar[i].nodeValue.match(/\s+$/)
45898             ){
45899                 ret.push("\n" + indent); 
45900             }
45901             
45902             
45903             
45904             
45905         }
45906         // what if all text?
45907         
45908         
45909         return ret.join('');
45910     },
45911     
45912          
45913         
45914     attr : function(node)
45915     {
45916         var attr = [];
45917         for(i = 0; i < node.attributes.length;i++) {
45918             
45919             // skip empty values?
45920             if (!node.attributes.item(i).value.length) {
45921                 continue;
45922             }
45923             attr.push(  node.attributes.item(i).name + '="' +
45924                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45925             );
45926         }
45927         return attr.length ? (' ' + attr.join(' ') ) : '';
45928         
45929     }
45930     
45931     
45932     
45933 }
45934 /**
45935  * @class Roo.htmleditor.KeyEnter
45936  * Handle Enter press..
45937  * @cfg {Roo.HtmlEditorCore} core the editor.
45938  * @constructor
45939  * Create a new Filter.
45940  * @param {Object} config Configuration options
45941  */
45942
45943
45944
45945 Roo.htmleditor.KeyEnter = function(cfg) {
45946     Roo.apply(this, cfg);
45947     // this does not actually call walk as it's really just a abstract class
45948  
45949     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45950 }
45951
45952
45953 Roo.htmleditor.KeyEnter.prototype = {
45954     
45955     core : false,
45956     
45957     keypress : function(e) {
45958         if (e.charCode != 13) {
45959             return true;
45960         }
45961         e.preventDefault();
45962         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45963         var doc = this.core.doc;
45964         
45965         var docFragment = doc.createDocumentFragment();
45966     
45967         //add a new line
45968         var newEle = doc.createTextNode('\n');
45969         docFragment.appendChild(newEle);
45970     
45971     
45972         var range = this.core.win.getSelection().getRangeAt(0);
45973         var n = range.commonAncestorContainer ;
45974         while (n && n.nodeType != 1) {
45975             n  = n.parentNode;
45976         }
45977         var li = false;
45978         if (n && n.tagName == 'UL') {
45979             li = doc.createElement('LI');
45980             n.appendChild(li);
45981             
45982         }
45983         if (n && n.tagName == 'LI') {
45984             li = doc.createElement('LI');
45985             if (n.nextSibling) {
45986                 n.parentNode.insertBefore(li, n.firstSibling);
45987                 
45988             } else {
45989                 n.parentNode.appendChild(li);
45990             }
45991         }
45992         if (li) {   
45993             range = doc.createRange();
45994             range.setStartAfter(li);
45995             range.collapse(true);
45996         
45997             //make the cursor there
45998             var sel = this.core.win.getSelection();
45999             sel.removeAllRanges();
46000             sel.addRange(range);
46001             return false;
46002             
46003             
46004         }
46005         //add the br, or p, or something else
46006         newEle = doc.createElement('br');
46007         docFragment.appendChild(newEle);
46008     
46009         //make the br replace selection
46010         
46011         range.deleteContents();
46012         
46013         range.insertNode(docFragment);
46014     
46015         //create a new range
46016         range = doc.createRange();
46017         range.setStartAfter(newEle);
46018         range.collapse(true);
46019     
46020         //make the cursor there
46021         var sel = this.core.win.getSelection();
46022         sel.removeAllRanges();
46023         sel.addRange(range);
46024     
46025         return false;
46026          
46027     }
46028 };
46029      
46030 /**
46031  * @class Roo.htmleditor.Block
46032  * Base class for html editor blocks - do not use it directly .. extend it..
46033  * @cfg {DomElement} node The node to apply stuff to.
46034  * @cfg {String} friendly_name the name that appears in the context bar about this block
46035  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46036  
46037  * @constructor
46038  * Create a new Filter.
46039  * @param {Object} config Configuration options
46040  */
46041
46042 Roo.htmleditor.Block  = function(cfg)
46043 {
46044     // do nothing .. should not be called really.
46045 }
46046
46047 Roo.htmleditor.Block.factory = function(node)
46048 {
46049     
46050     var id = Roo.get(node).id;
46051     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46052         Roo.htmleditor.Block.cache[id].readElement();
46053         return Roo.htmleditor.Block.cache[id];
46054     }
46055     
46056     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46057     if (typeof(cls) == 'undefined') {
46058         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46059         return false;
46060     }
46061     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46062     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46063 };
46064 // question goes here... do we need to clear out this cache sometimes?
46065 // or show we make it relivant to the htmleditor.
46066 Roo.htmleditor.Block.cache = {};
46067
46068 Roo.htmleditor.Block.prototype = {
46069     
46070     node : false,
46071     
46072      // used by context menu
46073     friendly_name : 'Image with caption',
46074     
46075     context : false,
46076     /**
46077      * Update a node with values from this object
46078      * @param {DomElement} node
46079      */
46080     updateElement : function(node)
46081     {
46082         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46083     },
46084      /**
46085      * convert to plain HTML for calling insertAtCursor..
46086      */
46087     toHTML : function()
46088     {
46089         return Roo.DomHelper.markup(this.toObject());
46090     },
46091     /**
46092      * used by readEleemnt to extract data from a node
46093      * may need improving as it's pretty basic
46094      
46095      * @param {DomElement} node
46096      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46097      * @param {String} attribute (use html - for contents, or style for using next param as style)
46098      * @param {String} style the style property - eg. text-align
46099      */
46100     getVal : function(node, tag, attr, style)
46101     {
46102         var n = node;
46103         if (tag !== true && n.tagName != tag.toUpperCase()) {
46104             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46105             // but kiss for now.
46106             n = node.getElementsByTagName(tag).item(0);
46107         }
46108         if (attr == 'html') {
46109             return n.innerHTML;
46110         }
46111         if (attr == 'style') {
46112             return Roo.get(n).getStyle(style);
46113         }
46114         
46115         return Roo.get(n).attr(attr);
46116             
46117     },
46118     /**
46119      * create a DomHelper friendly object - for use with 
46120      * Roo.DomHelper.markup / overwrite / etc..
46121      * (override this)
46122      */
46123     toObject : function()
46124     {
46125         return {};
46126     },
46127       /**
46128      * Read a node that has a 'data-block' property - and extract the values from it.
46129      * @param {DomElement} node - the node
46130      */
46131     readElement : function(node)
46132     {
46133         
46134     } 
46135     
46136     
46137 };
46138
46139  
46140
46141 /**
46142  * @class Roo.htmleditor.BlockFigure
46143  * Block that has an image and a figcaption
46144  * @cfg {String} image_src the url for the image
46145  * @cfg {String} align (left|right) alignment for the block default left
46146  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46147  * @cfg {String} caption the text to appear below  (and in the alt tag)
46148  * @cfg {String|number} image_width the width of the image number or %?
46149  * @cfg {String|number} image_height the height of the image number or %?
46150  * 
46151  * @constructor
46152  * Create a new Filter.
46153  * @param {Object} config Configuration options
46154  */
46155
46156 Roo.htmleditor.BlockFigure = function(cfg)
46157 {
46158     if (cfg.node) {
46159         this.readElement(cfg.node);
46160         this.updateElement(cfg.node);
46161     }
46162     Roo.apply(this, cfg);
46163 }
46164 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46165  
46166     
46167     // setable values.
46168     image_src: '',
46169     
46170     align: 'left',
46171     caption : '',
46172     text_align: 'left',
46173     
46174     width : '46%',
46175     margin: '2%',
46176     
46177     // used by context menu
46178     friendly_name : 'Image with caption',
46179     
46180     context : { // ?? static really
46181         width : {
46182             title: "Width",
46183             width: 40
46184             // ?? number
46185         },
46186         margin : {
46187             title: "Margin",
46188             width: 40
46189             // ?? number
46190         },
46191         align: {
46192             title: "Align",
46193             opts : [[ "left"],[ "right"]],
46194             width : 80
46195             
46196         },
46197         text_align: {
46198             title: "Caption Align",
46199             opts : [ [ "left"],[ "right"],[ "center"]],
46200             width : 80
46201         },
46202         
46203        
46204         image_src : {
46205             title: "Src",
46206             width: 220
46207         }
46208     },
46209     /**
46210      * create a DomHelper friendly object - for use with
46211      * Roo.DomHelper.markup / overwrite / etc..
46212      */
46213     toObject : function()
46214     {
46215         var d = document.createElement('div');
46216         d.innerHTML = this.caption;
46217         
46218         return {
46219             tag: 'figure',
46220             'data-block' : 'Figure',
46221             contenteditable : 'false',
46222             style : {
46223                 display: 'table',
46224                 float :  this.align ,
46225                 width :  this.width,
46226                 margin:  this.margin
46227             },
46228             cn : [
46229                 {
46230                     tag : 'img',
46231                     src : this.image_src,
46232                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46233                     style: {
46234                         width: '100%'
46235                     }
46236                 },
46237                 {
46238                     tag: 'figcaption',
46239                     contenteditable : true,
46240                     style : {
46241                         'text-align': this.text_align
46242                     },
46243                     html : this.caption
46244                     
46245                 }
46246             ]
46247         };
46248     },
46249     
46250     readElement : function(node)
46251     {
46252         this.image_src = this.getVal(node, 'img', 'src');
46253         this.align = this.getVal(node, 'figure', 'style', 'float');
46254         this.caption = this.getVal(node, 'figcaption', 'html');
46255         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46256         this.width = this.getVal(node, 'figure', 'style', 'width');
46257         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46258         
46259     } 
46260     
46261   
46262    
46263      
46264     
46265     
46266     
46267     
46268 })
46269
46270 //<script type="text/javascript">
46271
46272 /*
46273  * Based  Ext JS Library 1.1.1
46274  * Copyright(c) 2006-2007, Ext JS, LLC.
46275  * LGPL
46276  *
46277  */
46278  
46279 /**
46280  * @class Roo.HtmlEditorCore
46281  * @extends Roo.Component
46282  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46283  *
46284  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46285  */
46286
46287 Roo.HtmlEditorCore = function(config){
46288     
46289     
46290     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46291     
46292     
46293     this.addEvents({
46294         /**
46295          * @event initialize
46296          * Fires when the editor is fully initialized (including the iframe)
46297          * @param {Roo.HtmlEditorCore} this
46298          */
46299         initialize: true,
46300         /**
46301          * @event activate
46302          * Fires when the editor is first receives the focus. Any insertion must wait
46303          * until after this event.
46304          * @param {Roo.HtmlEditorCore} this
46305          */
46306         activate: true,
46307          /**
46308          * @event beforesync
46309          * Fires before the textarea is updated with content from the editor iframe. Return false
46310          * to cancel the sync.
46311          * @param {Roo.HtmlEditorCore} this
46312          * @param {String} html
46313          */
46314         beforesync: true,
46315          /**
46316          * @event beforepush
46317          * Fires before the iframe editor is updated with content from the textarea. Return false
46318          * to cancel the push.
46319          * @param {Roo.HtmlEditorCore} this
46320          * @param {String} html
46321          */
46322         beforepush: true,
46323          /**
46324          * @event sync
46325          * Fires when the textarea is updated with content from the editor iframe.
46326          * @param {Roo.HtmlEditorCore} this
46327          * @param {String} html
46328          */
46329         sync: true,
46330          /**
46331          * @event push
46332          * Fires when the iframe editor is updated with content from the textarea.
46333          * @param {Roo.HtmlEditorCore} this
46334          * @param {String} html
46335          */
46336         push: true,
46337         
46338         /**
46339          * @event editorevent
46340          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46341          * @param {Roo.HtmlEditorCore} this
46342          */
46343         editorevent: true
46344         
46345     });
46346     
46347     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46348     
46349     // defaults : white / black...
46350     this.applyBlacklists();
46351     
46352     
46353     
46354 };
46355
46356
46357 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46358
46359
46360      /**
46361      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46362      */
46363     
46364     owner : false,
46365     
46366      /**
46367      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46368      *                        Roo.resizable.
46369      */
46370     resizable : false,
46371      /**
46372      * @cfg {Number} height (in pixels)
46373      */   
46374     height: 300,
46375    /**
46376      * @cfg {Number} width (in pixels)
46377      */   
46378     width: 500,
46379     
46380     /**
46381      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46382      * 
46383      */
46384     stylesheets: false,
46385     
46386     /**
46387      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46388      */
46389     allowComments: false,
46390     // id of frame..
46391     frameId: false,
46392     
46393     // private properties
46394     validationEvent : false,
46395     deferHeight: true,
46396     initialized : false,
46397     activated : false,
46398     sourceEditMode : false,
46399     onFocus : Roo.emptyFn,
46400     iframePad:3,
46401     hideMode:'offsets',
46402     
46403     clearUp: true,
46404     
46405     // blacklist + whitelisted elements..
46406     black: false,
46407     white: false,
46408      
46409     bodyCls : '',
46410
46411     /**
46412      * Protected method that will not generally be called directly. It
46413      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46414      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46415      */
46416     getDocMarkup : function(){
46417         // body styles..
46418         var st = '';
46419         
46420         // inherit styels from page...?? 
46421         if (this.stylesheets === false) {
46422             
46423             Roo.get(document.head).select('style').each(function(node) {
46424                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46425             });
46426             
46427             Roo.get(document.head).select('link').each(function(node) { 
46428                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46429             });
46430             
46431         } else if (!this.stylesheets.length) {
46432                 // simple..
46433                 st = '<style type="text/css">' +
46434                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46435                    '</style>';
46436         } else {
46437             for (var i in this.stylesheets) {
46438                 if (typeof(this.stylesheets[i]) != 'string') {
46439                     continue;
46440                 }
46441                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46442             }
46443             
46444         }
46445         
46446         st +=  '<style type="text/css">' +
46447             'IMG { cursor: pointer } ' +
46448         '</style>';
46449
46450         var cls = 'roo-htmleditor-body';
46451         
46452         if(this.bodyCls.length){
46453             cls += ' ' + this.bodyCls;
46454         }
46455         
46456         return '<html><head>' + st  +
46457             //<style type="text/css">' +
46458             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46459             //'</style>' +
46460             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46461     },
46462
46463     // private
46464     onRender : function(ct, position)
46465     {
46466         var _t = this;
46467         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46468         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46469         
46470         
46471         this.el.dom.style.border = '0 none';
46472         this.el.dom.setAttribute('tabIndex', -1);
46473         this.el.addClass('x-hidden hide');
46474         
46475         
46476         
46477         if(Roo.isIE){ // fix IE 1px bogus margin
46478             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46479         }
46480        
46481         
46482         this.frameId = Roo.id();
46483         
46484          
46485         
46486         var iframe = this.owner.wrap.createChild({
46487             tag: 'iframe',
46488             cls: 'form-control', // bootstrap..
46489             id: this.frameId,
46490             name: this.frameId,
46491             frameBorder : 'no',
46492             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46493         }, this.el
46494         );
46495         
46496         
46497         this.iframe = iframe.dom;
46498
46499         this.assignDocWin();
46500         
46501         this.doc.designMode = 'on';
46502        
46503         this.doc.open();
46504         this.doc.write(this.getDocMarkup());
46505         this.doc.close();
46506
46507         
46508         var task = { // must defer to wait for browser to be ready
46509             run : function(){
46510                 //console.log("run task?" + this.doc.readyState);
46511                 this.assignDocWin();
46512                 if(this.doc.body || this.doc.readyState == 'complete'){
46513                     try {
46514                         this.doc.designMode="on";
46515                     } catch (e) {
46516                         return;
46517                     }
46518                     Roo.TaskMgr.stop(task);
46519                     this.initEditor.defer(10, this);
46520                 }
46521             },
46522             interval : 10,
46523             duration: 10000,
46524             scope: this
46525         };
46526         Roo.TaskMgr.start(task);
46527
46528     },
46529
46530     // private
46531     onResize : function(w, h)
46532     {
46533          Roo.log('resize: ' +w + ',' + h );
46534         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46535         if(!this.iframe){
46536             return;
46537         }
46538         if(typeof w == 'number'){
46539             
46540             this.iframe.style.width = w + 'px';
46541         }
46542         if(typeof h == 'number'){
46543             
46544             this.iframe.style.height = h + 'px';
46545             if(this.doc){
46546                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46547             }
46548         }
46549         
46550     },
46551
46552     /**
46553      * Toggles the editor between standard and source edit mode.
46554      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46555      */
46556     toggleSourceEdit : function(sourceEditMode){
46557         
46558         this.sourceEditMode = sourceEditMode === true;
46559         
46560         if(this.sourceEditMode){
46561  
46562             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46563             
46564         }else{
46565             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46566             //this.iframe.className = '';
46567             this.deferFocus();
46568         }
46569         //this.setSize(this.owner.wrap.getSize());
46570         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46571     },
46572
46573     
46574   
46575
46576     /**
46577      * Protected method that will not generally be called directly. If you need/want
46578      * custom HTML cleanup, this is the method you should override.
46579      * @param {String} html The HTML to be cleaned
46580      * return {String} The cleaned HTML
46581      */
46582     cleanHtml : function(html){
46583         html = String(html);
46584         if(html.length > 5){
46585             if(Roo.isSafari){ // strip safari nonsense
46586                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46587             }
46588         }
46589         if(html == '&nbsp;'){
46590             html = '';
46591         }
46592         return html;
46593     },
46594
46595     /**
46596      * HTML Editor -> Textarea
46597      * Protected method that will not generally be called directly. Syncs the contents
46598      * of the editor iframe with the textarea.
46599      */
46600     syncValue : function()
46601     {
46602         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46603         if(this.initialized){
46604             var bd = (this.doc.body || this.doc.documentElement);
46605             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46606             
46607             // not sure if this is really the place for this
46608             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46609             // this has to update attributes that get duped.. like alt and caption..
46610             
46611             
46612             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46613             //     Roo.htmleditor.Block.factory(e);
46614             //},this);
46615             
46616             
46617             var div = document.createElement('div');
46618             div.innerHTML = bd.innerHTML;
46619             // remove content editable. (blocks)
46620             
46621            
46622             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46623             //?? tidy?
46624             var html = div.innerHTML;
46625             if(Roo.isSafari){
46626                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46627                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46628                 if(m && m[1]){
46629                     html = '<div style="'+m[0]+'">' + html + '</div>';
46630                 }
46631             }
46632             html = this.cleanHtml(html);
46633             // fix up the special chars.. normaly like back quotes in word...
46634             // however we do not want to do this with chinese..
46635             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46636                 
46637                 var cc = match.charCodeAt();
46638
46639                 // Get the character value, handling surrogate pairs
46640                 if (match.length == 2) {
46641                     // It's a surrogate pair, calculate the Unicode code point
46642                     var high = match.charCodeAt(0) - 0xD800;
46643                     var low  = match.charCodeAt(1) - 0xDC00;
46644                     cc = (high * 0x400) + low + 0x10000;
46645                 }  else if (
46646                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46647                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46648                     (cc >= 0xf900 && cc < 0xfb00 )
46649                 ) {
46650                         return match;
46651                 }  
46652          
46653                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46654                 return "&#" + cc + ";";
46655                 
46656                 
46657             });
46658             
46659             
46660              
46661             if(this.owner.fireEvent('beforesync', this, html) !== false){
46662                 this.el.dom.value = html;
46663                 this.owner.fireEvent('sync', this, html);
46664             }
46665         }
46666     },
46667
46668     /**
46669      * TEXTAREA -> EDITABLE
46670      * Protected method that will not generally be called directly. Pushes the value of the textarea
46671      * into the iframe editor.
46672      */
46673     pushValue : function()
46674     {
46675         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46676         if(this.initialized){
46677             var v = this.el.dom.value.trim();
46678             
46679             
46680             if(this.owner.fireEvent('beforepush', this, v) !== false){
46681                 var d = (this.doc.body || this.doc.documentElement);
46682                 d.innerHTML = v;
46683                  
46684                 this.el.dom.value = d.innerHTML;
46685                 this.owner.fireEvent('push', this, v);
46686             }
46687             
46688             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46689                 
46690                 Roo.htmleditor.Block.factory(e);
46691                 
46692             },this);
46693             var lc = this.doc.body.lastChild;
46694             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46695                 // add an extra line at the end.
46696                 this.doc.body.appendChild(this.doc.createElement('br'));
46697             }
46698             
46699             
46700         }
46701     },
46702
46703     // private
46704     deferFocus : function(){
46705         this.focus.defer(10, this);
46706     },
46707
46708     // doc'ed in Field
46709     focus : function(){
46710         if(this.win && !this.sourceEditMode){
46711             this.win.focus();
46712         }else{
46713             this.el.focus();
46714         }
46715     },
46716     
46717     assignDocWin: function()
46718     {
46719         var iframe = this.iframe;
46720         
46721          if(Roo.isIE){
46722             this.doc = iframe.contentWindow.document;
46723             this.win = iframe.contentWindow;
46724         } else {
46725 //            if (!Roo.get(this.frameId)) {
46726 //                return;
46727 //            }
46728 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46729 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46730             
46731             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46732                 return;
46733             }
46734             
46735             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46736             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46737         }
46738     },
46739     
46740     // private
46741     initEditor : function(){
46742         //console.log("INIT EDITOR");
46743         this.assignDocWin();
46744         
46745         
46746         
46747         this.doc.designMode="on";
46748         this.doc.open();
46749         this.doc.write(this.getDocMarkup());
46750         this.doc.close();
46751         
46752         var dbody = (this.doc.body || this.doc.documentElement);
46753         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46754         // this copies styles from the containing element into thsi one..
46755         // not sure why we need all of this..
46756         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46757         
46758         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46759         //ss['background-attachment'] = 'fixed'; // w3c
46760         dbody.bgProperties = 'fixed'; // ie
46761         //Roo.DomHelper.applyStyles(dbody, ss);
46762         Roo.EventManager.on(this.doc, {
46763             //'mousedown': this.onEditorEvent,
46764             'mouseup': this.onEditorEvent,
46765             'dblclick': this.onEditorEvent,
46766             'click': this.onEditorEvent,
46767             'keyup': this.onEditorEvent,
46768             
46769             buffer:100,
46770             scope: this
46771         });
46772         Roo.EventManager.on(this.doc, {
46773             'paste': this.onPasteEvent,
46774             scope : this
46775         });
46776         if(Roo.isGecko){
46777             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46778         }
46779         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46780             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46781         }
46782         this.initialized = true;
46783
46784         
46785         // initialize special key events - enter
46786         new Roo.htmleditor.KeyEnter({core : this});
46787         
46788          
46789         
46790         this.owner.fireEvent('initialize', this);
46791         this.pushValue();
46792     },
46793     
46794     onPasteEvent : function(e,v)
46795     {
46796         // I think we better assume paste is going to be a dirty load of rubish from word..
46797         
46798         // even pasting into a 'email version' of this widget will have to clean up that mess.
46799         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46800         
46801         // check what type of paste - if it's an image, then handle it differently.
46802         if (cd.files.length > 0) {
46803             // pasting images?
46804             var urlAPI = (window.createObjectURL && window) || 
46805                 (window.URL && URL.revokeObjectURL && URL) || 
46806                 (window.webkitURL && webkitURL);
46807     
46808             var url = urlAPI.createObjectURL( cd.files[0]);
46809             this.insertAtCursor('<img src=" + url + ">');
46810             return false;
46811         }
46812         
46813         var html = cd.getData('text/html'); // clipboard event
46814         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46815         var images = parser.doc.getElementsByType('pict');
46816         Roo.log(images);
46817         //Roo.log(imgs);
46818         // fixme..
46819         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46820                        .map(function(g) { return g.toDataURL(); });
46821         
46822         
46823         html = this.cleanWordChars(html);
46824         
46825         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46826         
46827         if (images.length > 0) {
46828             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46829                 img.setAttribute('src', images[i]);
46830             });
46831         }
46832         
46833       
46834         new Roo.htmleditor.FilterStyleToTag({ node : d });
46835         new Roo.htmleditor.FilterAttributes({
46836             node : d,
46837             attrib_white : ['href', 'src', 'name', 'align'],
46838             attrib_clean : ['href', 'src' ] 
46839         });
46840         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46841         // should be fonts..
46842         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46843         new Roo.htmleditor.FilterParagraph({ node : d });
46844         new Roo.htmleditor.FilterSpan({ node : d });
46845         new Roo.htmleditor.FilterLongBr({ node : d });
46846         
46847         
46848         
46849         this.insertAtCursor(d.innerHTML);
46850         
46851         e.preventDefault();
46852         return false;
46853         // default behaveiour should be our local cleanup paste? (optional?)
46854         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46855         //this.owner.fireEvent('paste', e, v);
46856     },
46857     // private
46858     onDestroy : function(){
46859         
46860         
46861         
46862         if(this.rendered){
46863             
46864             //for (var i =0; i < this.toolbars.length;i++) {
46865             //    // fixme - ask toolbars for heights?
46866             //    this.toolbars[i].onDestroy();
46867            // }
46868             
46869             //this.wrap.dom.innerHTML = '';
46870             //this.wrap.remove();
46871         }
46872     },
46873
46874     // private
46875     onFirstFocus : function(){
46876         
46877         this.assignDocWin();
46878         
46879         
46880         this.activated = true;
46881          
46882     
46883         if(Roo.isGecko){ // prevent silly gecko errors
46884             this.win.focus();
46885             var s = this.win.getSelection();
46886             if(!s.focusNode || s.focusNode.nodeType != 3){
46887                 var r = s.getRangeAt(0);
46888                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46889                 r.collapse(true);
46890                 this.deferFocus();
46891             }
46892             try{
46893                 this.execCmd('useCSS', true);
46894                 this.execCmd('styleWithCSS', false);
46895             }catch(e){}
46896         }
46897         this.owner.fireEvent('activate', this);
46898     },
46899
46900     // private
46901     adjustFont: function(btn){
46902         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46903         //if(Roo.isSafari){ // safari
46904         //    adjust *= 2;
46905        // }
46906         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46907         if(Roo.isSafari){ // safari
46908             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46909             v =  (v < 10) ? 10 : v;
46910             v =  (v > 48) ? 48 : v;
46911             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46912             
46913         }
46914         
46915         
46916         v = Math.max(1, v+adjust);
46917         
46918         this.execCmd('FontSize', v  );
46919     },
46920
46921     onEditorEvent : function(e)
46922     {
46923         this.owner.fireEvent('editorevent', this, e);
46924       //  this.updateToolbar();
46925         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46926     },
46927
46928     insertTag : function(tg)
46929     {
46930         // could be a bit smarter... -> wrap the current selected tRoo..
46931         if (tg.toLowerCase() == 'span' ||
46932             tg.toLowerCase() == 'code' ||
46933             tg.toLowerCase() == 'sup' ||
46934             tg.toLowerCase() == 'sub' 
46935             ) {
46936             
46937             range = this.createRange(this.getSelection());
46938             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46939             wrappingNode.appendChild(range.extractContents());
46940             range.insertNode(wrappingNode);
46941
46942             return;
46943             
46944             
46945             
46946         }
46947         this.execCmd("formatblock",   tg);
46948         
46949     },
46950     
46951     insertText : function(txt)
46952     {
46953         
46954         
46955         var range = this.createRange();
46956         range.deleteContents();
46957                //alert(Sender.getAttribute('label'));
46958                
46959         range.insertNode(this.doc.createTextNode(txt));
46960     } ,
46961     
46962      
46963
46964     /**
46965      * Executes a Midas editor command on the editor document and performs necessary focus and
46966      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46967      * @param {String} cmd The Midas command
46968      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46969      */
46970     relayCmd : function(cmd, value){
46971         this.win.focus();
46972         this.execCmd(cmd, value);
46973         this.owner.fireEvent('editorevent', this);
46974         //this.updateToolbar();
46975         this.owner.deferFocus();
46976     },
46977
46978     /**
46979      * Executes a Midas editor command directly on the editor document.
46980      * For visual commands, you should use {@link #relayCmd} instead.
46981      * <b>This should only be called after the editor is initialized.</b>
46982      * @param {String} cmd The Midas command
46983      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46984      */
46985     execCmd : function(cmd, value){
46986         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46987         this.syncValue();
46988     },
46989  
46990  
46991    
46992     /**
46993      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46994      * to insert tRoo.
46995      * @param {String} text | dom node.. 
46996      */
46997     insertAtCursor : function(text)
46998     {
46999         
47000         if(!this.activated){
47001             return;
47002         }
47003          
47004         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47005             this.win.focus();
47006             
47007             
47008             // from jquery ui (MIT licenced)
47009             var range, node;
47010             var win = this.win;
47011             
47012             if (win.getSelection && win.getSelection().getRangeAt) {
47013                 
47014                 // delete the existing?
47015                 
47016                 this.createRange(this.getSelection()).deleteContents();
47017                 range = win.getSelection().getRangeAt(0);
47018                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47019                 range.insertNode(node);
47020             } else if (win.document.selection && win.document.selection.createRange) {
47021                 // no firefox support
47022                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47023                 win.document.selection.createRange().pasteHTML(txt);
47024             } else {
47025                 // no firefox support
47026                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47027                 this.execCmd('InsertHTML', txt);
47028             } 
47029             
47030             this.syncValue();
47031             
47032             this.deferFocus();
47033         }
47034     },
47035  // private
47036     mozKeyPress : function(e){
47037         if(e.ctrlKey){
47038             var c = e.getCharCode(), cmd;
47039           
47040             if(c > 0){
47041                 c = String.fromCharCode(c).toLowerCase();
47042                 switch(c){
47043                     case 'b':
47044                         cmd = 'bold';
47045                         break;
47046                     case 'i':
47047                         cmd = 'italic';
47048                         break;
47049                     
47050                     case 'u':
47051                         cmd = 'underline';
47052                         break;
47053                     
47054                     //case 'v':
47055                       //  this.cleanUpPaste.defer(100, this);
47056                       //  return;
47057                         
47058                 }
47059                 if(cmd){
47060                     this.win.focus();
47061                     this.execCmd(cmd);
47062                     this.deferFocus();
47063                     e.preventDefault();
47064                 }
47065                 
47066             }
47067         }
47068     },
47069
47070     // private
47071     fixKeys : function(){ // load time branching for fastest keydown performance
47072         if(Roo.isIE){
47073             return function(e){
47074                 var k = e.getKey(), r;
47075                 if(k == e.TAB){
47076                     e.stopEvent();
47077                     r = this.doc.selection.createRange();
47078                     if(r){
47079                         r.collapse(true);
47080                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47081                         this.deferFocus();
47082                     }
47083                     return;
47084                 }
47085                 
47086                 if(k == e.ENTER){
47087                     r = this.doc.selection.createRange();
47088                     if(r){
47089                         var target = r.parentElement();
47090                         if(!target || target.tagName.toLowerCase() != 'li'){
47091                             e.stopEvent();
47092                             r.pasteHTML('<br/>');
47093                             r.collapse(false);
47094                             r.select();
47095                         }
47096                     }
47097                 }
47098                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47099                 //    this.cleanUpPaste.defer(100, this);
47100                 //    return;
47101                 //}
47102                 
47103                 
47104             };
47105         }else if(Roo.isOpera){
47106             return function(e){
47107                 var k = e.getKey();
47108                 if(k == e.TAB){
47109                     e.stopEvent();
47110                     this.win.focus();
47111                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47112                     this.deferFocus();
47113                 }
47114                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47115                 //    this.cleanUpPaste.defer(100, this);
47116                  //   return;
47117                 //}
47118                 
47119             };
47120         }else if(Roo.isSafari){
47121             return function(e){
47122                 var k = e.getKey();
47123                 
47124                 if(k == e.TAB){
47125                     e.stopEvent();
47126                     this.execCmd('InsertText','\t');
47127                     this.deferFocus();
47128                     return;
47129                 }
47130                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47131                  //   this.cleanUpPaste.defer(100, this);
47132                  //   return;
47133                // }
47134                 
47135              };
47136         }
47137     }(),
47138     
47139     getAllAncestors: function()
47140     {
47141         var p = this.getSelectedNode();
47142         var a = [];
47143         if (!p) {
47144             a.push(p); // push blank onto stack..
47145             p = this.getParentElement();
47146         }
47147         
47148         
47149         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47150             a.push(p);
47151             p = p.parentNode;
47152         }
47153         a.push(this.doc.body);
47154         return a;
47155     },
47156     lastSel : false,
47157     lastSelNode : false,
47158     
47159     
47160     getSelection : function() 
47161     {
47162         this.assignDocWin();
47163         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47164     },
47165     /**
47166      * Select a dom node
47167      * @param {DomElement} node the node to select
47168      */
47169     selectNode : function(node)
47170     {
47171         
47172             var nodeRange = node.ownerDocument.createRange();
47173             try {
47174                 nodeRange.selectNode(node);
47175             } catch (e) {
47176                 nodeRange.selectNodeContents(node);
47177             }
47178             //nodeRange.collapse(true);
47179             var s = this.win.getSelection();
47180             s.removeAllRanges();
47181             s.addRange(nodeRange);
47182     },
47183     
47184     getSelectedNode: function() 
47185     {
47186         // this may only work on Gecko!!!
47187         
47188         // should we cache this!!!!
47189         
47190         
47191         
47192          
47193         var range = this.createRange(this.getSelection()).cloneRange();
47194         
47195         if (Roo.isIE) {
47196             var parent = range.parentElement();
47197             while (true) {
47198                 var testRange = range.duplicate();
47199                 testRange.moveToElementText(parent);
47200                 if (testRange.inRange(range)) {
47201                     break;
47202                 }
47203                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47204                     break;
47205                 }
47206                 parent = parent.parentElement;
47207             }
47208             return parent;
47209         }
47210         
47211         // is ancestor a text element.
47212         var ac =  range.commonAncestorContainer;
47213         if (ac.nodeType == 3) {
47214             ac = ac.parentNode;
47215         }
47216         
47217         var ar = ac.childNodes;
47218          
47219         var nodes = [];
47220         var other_nodes = [];
47221         var has_other_nodes = false;
47222         for (var i=0;i<ar.length;i++) {
47223             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47224                 continue;
47225             }
47226             // fullly contained node.
47227             
47228             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47229                 nodes.push(ar[i]);
47230                 continue;
47231             }
47232             
47233             // probably selected..
47234             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47235                 other_nodes.push(ar[i]);
47236                 continue;
47237             }
47238             // outer..
47239             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47240                 continue;
47241             }
47242             
47243             
47244             has_other_nodes = true;
47245         }
47246         if (!nodes.length && other_nodes.length) {
47247             nodes= other_nodes;
47248         }
47249         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47250             return false;
47251         }
47252         
47253         return nodes[0];
47254     },
47255     createRange: function(sel)
47256     {
47257         // this has strange effects when using with 
47258         // top toolbar - not sure if it's a great idea.
47259         //this.editor.contentWindow.focus();
47260         if (typeof sel != "undefined") {
47261             try {
47262                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47263             } catch(e) {
47264                 return this.doc.createRange();
47265             }
47266         } else {
47267             return this.doc.createRange();
47268         }
47269     },
47270     getParentElement: function()
47271     {
47272         
47273         this.assignDocWin();
47274         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47275         
47276         var range = this.createRange(sel);
47277          
47278         try {
47279             var p = range.commonAncestorContainer;
47280             while (p.nodeType == 3) { // text node
47281                 p = p.parentNode;
47282             }
47283             return p;
47284         } catch (e) {
47285             return null;
47286         }
47287     
47288     },
47289     /***
47290      *
47291      * Range intersection.. the hard stuff...
47292      *  '-1' = before
47293      *  '0' = hits..
47294      *  '1' = after.
47295      *         [ -- selected range --- ]
47296      *   [fail]                        [fail]
47297      *
47298      *    basically..
47299      *      if end is before start or  hits it. fail.
47300      *      if start is after end or hits it fail.
47301      *
47302      *   if either hits (but other is outside. - then it's not 
47303      *   
47304      *    
47305      **/
47306     
47307     
47308     // @see http://www.thismuchiknow.co.uk/?p=64.
47309     rangeIntersectsNode : function(range, node)
47310     {
47311         var nodeRange = node.ownerDocument.createRange();
47312         try {
47313             nodeRange.selectNode(node);
47314         } catch (e) {
47315             nodeRange.selectNodeContents(node);
47316         }
47317     
47318         var rangeStartRange = range.cloneRange();
47319         rangeStartRange.collapse(true);
47320     
47321         var rangeEndRange = range.cloneRange();
47322         rangeEndRange.collapse(false);
47323     
47324         var nodeStartRange = nodeRange.cloneRange();
47325         nodeStartRange.collapse(true);
47326     
47327         var nodeEndRange = nodeRange.cloneRange();
47328         nodeEndRange.collapse(false);
47329     
47330         return rangeStartRange.compareBoundaryPoints(
47331                  Range.START_TO_START, nodeEndRange) == -1 &&
47332                rangeEndRange.compareBoundaryPoints(
47333                  Range.START_TO_START, nodeStartRange) == 1;
47334         
47335          
47336     },
47337     rangeCompareNode : function(range, node)
47338     {
47339         var nodeRange = node.ownerDocument.createRange();
47340         try {
47341             nodeRange.selectNode(node);
47342         } catch (e) {
47343             nodeRange.selectNodeContents(node);
47344         }
47345         
47346         
47347         range.collapse(true);
47348     
47349         nodeRange.collapse(true);
47350      
47351         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47352         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47353          
47354         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47355         
47356         var nodeIsBefore   =  ss == 1;
47357         var nodeIsAfter    = ee == -1;
47358         
47359         if (nodeIsBefore && nodeIsAfter) {
47360             return 0; // outer
47361         }
47362         if (!nodeIsBefore && nodeIsAfter) {
47363             return 1; //right trailed.
47364         }
47365         
47366         if (nodeIsBefore && !nodeIsAfter) {
47367             return 2;  // left trailed.
47368         }
47369         // fully contined.
47370         return 3;
47371     },
47372  
47373     cleanWordChars : function(input) {// change the chars to hex code
47374         
47375        var swapCodes  = [ 
47376             [    8211, "&#8211;" ], 
47377             [    8212, "&#8212;" ], 
47378             [    8216,  "'" ],  
47379             [    8217, "'" ],  
47380             [    8220, '"' ],  
47381             [    8221, '"' ],  
47382             [    8226, "*" ],  
47383             [    8230, "..." ]
47384         ]; 
47385         var output = input;
47386         Roo.each(swapCodes, function(sw) { 
47387             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47388             
47389             output = output.replace(swapper, sw[1]);
47390         });
47391         
47392         return output;
47393     },
47394     
47395      
47396     
47397         
47398     
47399     cleanUpChild : function (node)
47400     {
47401         
47402         new Roo.htmleditor.FilterComment({node : node});
47403         new Roo.htmleditor.FilterAttributes({
47404                 node : node,
47405                 attrib_black : this.ablack,
47406                 attrib_clean : this.aclean,
47407                 style_white : this.cwhite,
47408                 style_black : this.cblack
47409         });
47410         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47411         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47412          
47413         
47414     },
47415     
47416     /**
47417      * Clean up MS wordisms...
47418      * @deprecated - use filter directly
47419      */
47420     cleanWord : function(node)
47421     {
47422         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47423         
47424     },
47425    
47426     
47427     /**
47428
47429      * @deprecated - use filters
47430      */
47431     cleanTableWidths : function(node)
47432     {
47433         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47434         
47435  
47436     },
47437     
47438      
47439         
47440     applyBlacklists : function()
47441     {
47442         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47443         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47444         
47445         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47446         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47447         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47448         
47449         this.white = [];
47450         this.black = [];
47451         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47452             if (b.indexOf(tag) > -1) {
47453                 return;
47454             }
47455             this.white.push(tag);
47456             
47457         }, this);
47458         
47459         Roo.each(w, function(tag) {
47460             if (b.indexOf(tag) > -1) {
47461                 return;
47462             }
47463             if (this.white.indexOf(tag) > -1) {
47464                 return;
47465             }
47466             this.white.push(tag);
47467             
47468         }, this);
47469         
47470         
47471         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47472             if (w.indexOf(tag) > -1) {
47473                 return;
47474             }
47475             this.black.push(tag);
47476             
47477         }, this);
47478         
47479         Roo.each(b, function(tag) {
47480             if (w.indexOf(tag) > -1) {
47481                 return;
47482             }
47483             if (this.black.indexOf(tag) > -1) {
47484                 return;
47485             }
47486             this.black.push(tag);
47487             
47488         }, this);
47489         
47490         
47491         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47492         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47493         
47494         this.cwhite = [];
47495         this.cblack = [];
47496         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47497             if (b.indexOf(tag) > -1) {
47498                 return;
47499             }
47500             this.cwhite.push(tag);
47501             
47502         }, this);
47503         
47504         Roo.each(w, function(tag) {
47505             if (b.indexOf(tag) > -1) {
47506                 return;
47507             }
47508             if (this.cwhite.indexOf(tag) > -1) {
47509                 return;
47510             }
47511             this.cwhite.push(tag);
47512             
47513         }, this);
47514         
47515         
47516         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47517             if (w.indexOf(tag) > -1) {
47518                 return;
47519             }
47520             this.cblack.push(tag);
47521             
47522         }, this);
47523         
47524         Roo.each(b, function(tag) {
47525             if (w.indexOf(tag) > -1) {
47526                 return;
47527             }
47528             if (this.cblack.indexOf(tag) > -1) {
47529                 return;
47530             }
47531             this.cblack.push(tag);
47532             
47533         }, this);
47534     },
47535     
47536     setStylesheets : function(stylesheets)
47537     {
47538         if(typeof(stylesheets) == 'string'){
47539             Roo.get(this.iframe.contentDocument.head).createChild({
47540                 tag : 'link',
47541                 rel : 'stylesheet',
47542                 type : 'text/css',
47543                 href : stylesheets
47544             });
47545             
47546             return;
47547         }
47548         var _this = this;
47549      
47550         Roo.each(stylesheets, function(s) {
47551             if(!s.length){
47552                 return;
47553             }
47554             
47555             Roo.get(_this.iframe.contentDocument.head).createChild({
47556                 tag : 'link',
47557                 rel : 'stylesheet',
47558                 type : 'text/css',
47559                 href : s
47560             });
47561         });
47562
47563         
47564     },
47565     
47566     removeStylesheets : function()
47567     {
47568         var _this = this;
47569         
47570         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47571             s.remove();
47572         });
47573     },
47574     
47575     setStyle : function(style)
47576     {
47577         Roo.get(this.iframe.contentDocument.head).createChild({
47578             tag : 'style',
47579             type : 'text/css',
47580             html : style
47581         });
47582
47583         return;
47584     }
47585     
47586     // hide stuff that is not compatible
47587     /**
47588      * @event blur
47589      * @hide
47590      */
47591     /**
47592      * @event change
47593      * @hide
47594      */
47595     /**
47596      * @event focus
47597      * @hide
47598      */
47599     /**
47600      * @event specialkey
47601      * @hide
47602      */
47603     /**
47604      * @cfg {String} fieldClass @hide
47605      */
47606     /**
47607      * @cfg {String} focusClass @hide
47608      */
47609     /**
47610      * @cfg {String} autoCreate @hide
47611      */
47612     /**
47613      * @cfg {String} inputType @hide
47614      */
47615     /**
47616      * @cfg {String} invalidClass @hide
47617      */
47618     /**
47619      * @cfg {String} invalidText @hide
47620      */
47621     /**
47622      * @cfg {String} msgFx @hide
47623      */
47624     /**
47625      * @cfg {String} validateOnBlur @hide
47626      */
47627 });
47628
47629 Roo.HtmlEditorCore.white = [
47630         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47631         
47632        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47633        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47634        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47635        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47636        'TABLE',   'UL',         'XMP', 
47637        
47638        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47639       'THEAD',   'TR', 
47640      
47641       'DIR', 'MENU', 'OL', 'UL', 'DL',
47642        
47643       'EMBED',  'OBJECT'
47644 ];
47645
47646
47647 Roo.HtmlEditorCore.black = [
47648     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47649         'APPLET', // 
47650         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47651         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47652         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47653         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47654         //'FONT' // CLEAN LATER..
47655         'COLGROUP', 'COL'  // messy tables.
47656         
47657 ];
47658 Roo.HtmlEditorCore.clean = [ // ?? needed???
47659      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47660 ];
47661 Roo.HtmlEditorCore.tag_remove = [
47662     'FONT', 'TBODY'  
47663 ];
47664 // attributes..
47665
47666 Roo.HtmlEditorCore.ablack = [
47667     'on'
47668 ];
47669     
47670 Roo.HtmlEditorCore.aclean = [ 
47671     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47672 ];
47673
47674 // protocols..
47675 Roo.HtmlEditorCore.pwhite= [
47676         'http',  'https',  'mailto'
47677 ];
47678
47679 // white listed style attributes.
47680 Roo.HtmlEditorCore.cwhite= [
47681       //  'text-align', /// default is to allow most things..
47682       
47683          
47684 //        'font-size'//??
47685 ];
47686
47687 // black listed style attributes.
47688 Roo.HtmlEditorCore.cblack= [
47689       //  'font-size' -- this can be set by the project 
47690 ];
47691
47692
47693
47694
47695     //<script type="text/javascript">
47696
47697 /*
47698  * Ext JS Library 1.1.1
47699  * Copyright(c) 2006-2007, Ext JS, LLC.
47700  * Licence LGPL
47701  * 
47702  */
47703  
47704  
47705 Roo.form.HtmlEditor = function(config){
47706     
47707     
47708     
47709     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47710     
47711     if (!this.toolbars) {
47712         this.toolbars = [];
47713     }
47714     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47715     
47716     
47717 };
47718
47719 /**
47720  * @class Roo.form.HtmlEditor
47721  * @extends Roo.form.Field
47722  * Provides a lightweight HTML Editor component.
47723  *
47724  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47725  * 
47726  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47727  * supported by this editor.</b><br/><br/>
47728  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47729  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47730  */
47731 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47732     /**
47733      * @cfg {Boolean} clearUp
47734      */
47735     clearUp : true,
47736       /**
47737      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47738      */
47739     toolbars : false,
47740    
47741      /**
47742      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47743      *                        Roo.resizable.
47744      */
47745     resizable : false,
47746      /**
47747      * @cfg {Number} height (in pixels)
47748      */   
47749     height: 300,
47750    /**
47751      * @cfg {Number} width (in pixels)
47752      */   
47753     width: 500,
47754     
47755     /**
47756      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47757      * 
47758      */
47759     stylesheets: false,
47760     
47761     
47762      /**
47763      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47764      * 
47765      */
47766     cblack: false,
47767     /**
47768      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47769      * 
47770      */
47771     cwhite: false,
47772     
47773      /**
47774      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47775      * 
47776      */
47777     black: false,
47778     /**
47779      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47780      * 
47781      */
47782     white: false,
47783     /**
47784      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47785      */
47786     allowComments: false,
47787     /**
47788      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47789      */
47790     
47791     
47792      bodyCls : '',
47793     
47794     // id of frame..
47795     frameId: false,
47796     
47797     // private properties
47798     validationEvent : false,
47799     deferHeight: true,
47800     initialized : false,
47801     activated : false,
47802     
47803     onFocus : Roo.emptyFn,
47804     iframePad:3,
47805     hideMode:'offsets',
47806     
47807     actionMode : 'container', // defaults to hiding it...
47808     
47809     defaultAutoCreate : { // modified by initCompnoent..
47810         tag: "textarea",
47811         style:"width:500px;height:300px;",
47812         autocomplete: "new-password"
47813     },
47814
47815     // private
47816     initComponent : function(){
47817         this.addEvents({
47818             /**
47819              * @event initialize
47820              * Fires when the editor is fully initialized (including the iframe)
47821              * @param {HtmlEditor} this
47822              */
47823             initialize: true,
47824             /**
47825              * @event activate
47826              * Fires when the editor is first receives the focus. Any insertion must wait
47827              * until after this event.
47828              * @param {HtmlEditor} this
47829              */
47830             activate: true,
47831              /**
47832              * @event beforesync
47833              * Fires before the textarea is updated with content from the editor iframe. Return false
47834              * to cancel the sync.
47835              * @param {HtmlEditor} this
47836              * @param {String} html
47837              */
47838             beforesync: true,
47839              /**
47840              * @event beforepush
47841              * Fires before the iframe editor is updated with content from the textarea. Return false
47842              * to cancel the push.
47843              * @param {HtmlEditor} this
47844              * @param {String} html
47845              */
47846             beforepush: true,
47847              /**
47848              * @event sync
47849              * Fires when the textarea is updated with content from the editor iframe.
47850              * @param {HtmlEditor} this
47851              * @param {String} html
47852              */
47853             sync: true,
47854              /**
47855              * @event push
47856              * Fires when the iframe editor is updated with content from the textarea.
47857              * @param {HtmlEditor} this
47858              * @param {String} html
47859              */
47860             push: true,
47861              /**
47862              * @event editmodechange
47863              * Fires when the editor switches edit modes
47864              * @param {HtmlEditor} this
47865              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47866              */
47867             editmodechange: true,
47868             /**
47869              * @event editorevent
47870              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47871              * @param {HtmlEditor} this
47872              */
47873             editorevent: true,
47874             /**
47875              * @event firstfocus
47876              * Fires when on first focus - needed by toolbars..
47877              * @param {HtmlEditor} this
47878              */
47879             firstfocus: true,
47880             /**
47881              * @event autosave
47882              * Auto save the htmlEditor value as a file into Events
47883              * @param {HtmlEditor} this
47884              */
47885             autosave: true,
47886             /**
47887              * @event savedpreview
47888              * preview the saved version of htmlEditor
47889              * @param {HtmlEditor} this
47890              */
47891             savedpreview: true,
47892             
47893             /**
47894             * @event stylesheetsclick
47895             * Fires when press the Sytlesheets button
47896             * @param {Roo.HtmlEditorCore} this
47897             */
47898             stylesheetsclick: true,
47899             /**
47900             * @event paste
47901             * Fires when press user pastes into the editor
47902             * @param {Roo.HtmlEditorCore} this
47903             */
47904             paste: true 
47905         });
47906         this.defaultAutoCreate =  {
47907             tag: "textarea",
47908             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47909             autocomplete: "new-password"
47910         };
47911     },
47912
47913     /**
47914      * Protected method that will not generally be called directly. It
47915      * is called when the editor creates its toolbar. Override this method if you need to
47916      * add custom toolbar buttons.
47917      * @param {HtmlEditor} editor
47918      */
47919     createToolbar : function(editor){
47920         Roo.log("create toolbars");
47921         if (!editor.toolbars || !editor.toolbars.length) {
47922             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47923         }
47924         
47925         for (var i =0 ; i < editor.toolbars.length;i++) {
47926             editor.toolbars[i] = Roo.factory(
47927                     typeof(editor.toolbars[i]) == 'string' ?
47928                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47929                 Roo.form.HtmlEditor);
47930             editor.toolbars[i].init(editor);
47931         }
47932          
47933         
47934     },
47935
47936      
47937     // private
47938     onRender : function(ct, position)
47939     {
47940         var _t = this;
47941         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47942         
47943         this.wrap = this.el.wrap({
47944             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47945         });
47946         
47947         this.editorcore.onRender(ct, position);
47948          
47949         if (this.resizable) {
47950             this.resizeEl = new Roo.Resizable(this.wrap, {
47951                 pinned : true,
47952                 wrap: true,
47953                 dynamic : true,
47954                 minHeight : this.height,
47955                 height: this.height,
47956                 handles : this.resizable,
47957                 width: this.width,
47958                 listeners : {
47959                     resize : function(r, w, h) {
47960                         _t.onResize(w,h); // -something
47961                     }
47962                 }
47963             });
47964             
47965         }
47966         this.createToolbar(this);
47967        
47968         
47969         if(!this.width){
47970             this.setSize(this.wrap.getSize());
47971         }
47972         if (this.resizeEl) {
47973             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47974             // should trigger onReize..
47975         }
47976         
47977         this.keyNav = new Roo.KeyNav(this.el, {
47978             
47979             "tab" : function(e){
47980                 e.preventDefault();
47981                 
47982                 var value = this.getValue();
47983                 
47984                 var start = this.el.dom.selectionStart;
47985                 var end = this.el.dom.selectionEnd;
47986                 
47987                 if(!e.shiftKey){
47988                     
47989                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47990                     this.el.dom.setSelectionRange(end + 1, end + 1);
47991                     return;
47992                 }
47993                 
47994                 var f = value.substring(0, start).split("\t");
47995                 
47996                 if(f.pop().length != 0){
47997                     return;
47998                 }
47999                 
48000                 this.setValue(f.join("\t") + value.substring(end));
48001                 this.el.dom.setSelectionRange(start - 1, start - 1);
48002                 
48003             },
48004             
48005             "home" : function(e){
48006                 e.preventDefault();
48007                 
48008                 var curr = this.el.dom.selectionStart;
48009                 var lines = this.getValue().split("\n");
48010                 
48011                 if(!lines.length){
48012                     return;
48013                 }
48014                 
48015                 if(e.ctrlKey){
48016                     this.el.dom.setSelectionRange(0, 0);
48017                     return;
48018                 }
48019                 
48020                 var pos = 0;
48021                 
48022                 for (var i = 0; i < lines.length;i++) {
48023                     pos += lines[i].length;
48024                     
48025                     if(i != 0){
48026                         pos += 1;
48027                     }
48028                     
48029                     if(pos < curr){
48030                         continue;
48031                     }
48032                     
48033                     pos -= lines[i].length;
48034                     
48035                     break;
48036                 }
48037                 
48038                 if(!e.shiftKey){
48039                     this.el.dom.setSelectionRange(pos, pos);
48040                     return;
48041                 }
48042                 
48043                 this.el.dom.selectionStart = pos;
48044                 this.el.dom.selectionEnd = curr;
48045             },
48046             
48047             "end" : function(e){
48048                 e.preventDefault();
48049                 
48050                 var curr = this.el.dom.selectionStart;
48051                 var lines = this.getValue().split("\n");
48052                 
48053                 if(!lines.length){
48054                     return;
48055                 }
48056                 
48057                 if(e.ctrlKey){
48058                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48059                     return;
48060                 }
48061                 
48062                 var pos = 0;
48063                 
48064                 for (var i = 0; i < lines.length;i++) {
48065                     
48066                     pos += lines[i].length;
48067                     
48068                     if(i != 0){
48069                         pos += 1;
48070                     }
48071                     
48072                     if(pos < curr){
48073                         continue;
48074                     }
48075                     
48076                     break;
48077                 }
48078                 
48079                 if(!e.shiftKey){
48080                     this.el.dom.setSelectionRange(pos, pos);
48081                     return;
48082                 }
48083                 
48084                 this.el.dom.selectionStart = curr;
48085                 this.el.dom.selectionEnd = pos;
48086             },
48087
48088             scope : this,
48089
48090             doRelay : function(foo, bar, hname){
48091                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48092             },
48093
48094             forceKeyDown: true
48095         });
48096         
48097 //        if(this.autosave && this.w){
48098 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48099 //        }
48100     },
48101
48102     // private
48103     onResize : function(w, h)
48104     {
48105         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48106         var ew = false;
48107         var eh = false;
48108         
48109         if(this.el ){
48110             if(typeof w == 'number'){
48111                 var aw = w - this.wrap.getFrameWidth('lr');
48112                 this.el.setWidth(this.adjustWidth('textarea', aw));
48113                 ew = aw;
48114             }
48115             if(typeof h == 'number'){
48116                 var tbh = 0;
48117                 for (var i =0; i < this.toolbars.length;i++) {
48118                     // fixme - ask toolbars for heights?
48119                     tbh += this.toolbars[i].tb.el.getHeight();
48120                     if (this.toolbars[i].footer) {
48121                         tbh += this.toolbars[i].footer.el.getHeight();
48122                     }
48123                 }
48124                 
48125                 
48126                 
48127                 
48128                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48129                 ah -= 5; // knock a few pixes off for look..
48130 //                Roo.log(ah);
48131                 this.el.setHeight(this.adjustWidth('textarea', ah));
48132                 var eh = ah;
48133             }
48134         }
48135         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48136         this.editorcore.onResize(ew,eh);
48137         
48138     },
48139
48140     /**
48141      * Toggles the editor between standard and source edit mode.
48142      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48143      */
48144     toggleSourceEdit : function(sourceEditMode)
48145     {
48146         this.editorcore.toggleSourceEdit(sourceEditMode);
48147         
48148         if(this.editorcore.sourceEditMode){
48149             Roo.log('editor - showing textarea');
48150             
48151 //            Roo.log('in');
48152 //            Roo.log(this.syncValue());
48153             this.editorcore.syncValue();
48154             this.el.removeClass('x-hidden');
48155             this.el.dom.removeAttribute('tabIndex');
48156             this.el.focus();
48157             this.el.dom.scrollTop = 0;
48158             
48159             
48160             for (var i = 0; i < this.toolbars.length; i++) {
48161                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48162                     this.toolbars[i].tb.hide();
48163                     this.toolbars[i].footer.hide();
48164                 }
48165             }
48166             
48167         }else{
48168             Roo.log('editor - hiding textarea');
48169 //            Roo.log('out')
48170 //            Roo.log(this.pushValue()); 
48171             this.editorcore.pushValue();
48172             
48173             this.el.addClass('x-hidden');
48174             this.el.dom.setAttribute('tabIndex', -1);
48175             
48176             for (var i = 0; i < this.toolbars.length; i++) {
48177                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48178                     this.toolbars[i].tb.show();
48179                     this.toolbars[i].footer.show();
48180                 }
48181             }
48182             
48183             //this.deferFocus();
48184         }
48185         
48186         this.setSize(this.wrap.getSize());
48187         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48188         
48189         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48190     },
48191  
48192     // private (for BoxComponent)
48193     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48194
48195     // private (for BoxComponent)
48196     getResizeEl : function(){
48197         return this.wrap;
48198     },
48199
48200     // private (for BoxComponent)
48201     getPositionEl : function(){
48202         return this.wrap;
48203     },
48204
48205     // private
48206     initEvents : function(){
48207         this.originalValue = this.getValue();
48208     },
48209
48210     /**
48211      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48212      * @method
48213      */
48214     markInvalid : Roo.emptyFn,
48215     /**
48216      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48217      * @method
48218      */
48219     clearInvalid : Roo.emptyFn,
48220
48221     setValue : function(v){
48222         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48223         this.editorcore.pushValue();
48224     },
48225
48226      
48227     // private
48228     deferFocus : function(){
48229         this.focus.defer(10, this);
48230     },
48231
48232     // doc'ed in Field
48233     focus : function(){
48234         this.editorcore.focus();
48235         
48236     },
48237       
48238
48239     // private
48240     onDestroy : function(){
48241         
48242         
48243         
48244         if(this.rendered){
48245             
48246             for (var i =0; i < this.toolbars.length;i++) {
48247                 // fixme - ask toolbars for heights?
48248                 this.toolbars[i].onDestroy();
48249             }
48250             
48251             this.wrap.dom.innerHTML = '';
48252             this.wrap.remove();
48253         }
48254     },
48255
48256     // private
48257     onFirstFocus : function(){
48258         //Roo.log("onFirstFocus");
48259         this.editorcore.onFirstFocus();
48260          for (var i =0; i < this.toolbars.length;i++) {
48261             this.toolbars[i].onFirstFocus();
48262         }
48263         
48264     },
48265     
48266     // private
48267     syncValue : function()
48268     {
48269         this.editorcore.syncValue();
48270     },
48271     
48272     pushValue : function()
48273     {
48274         this.editorcore.pushValue();
48275     },
48276     
48277     setStylesheets : function(stylesheets)
48278     {
48279         this.editorcore.setStylesheets(stylesheets);
48280     },
48281     
48282     removeStylesheets : function()
48283     {
48284         this.editorcore.removeStylesheets();
48285     }
48286      
48287     
48288     // hide stuff that is not compatible
48289     /**
48290      * @event blur
48291      * @hide
48292      */
48293     /**
48294      * @event change
48295      * @hide
48296      */
48297     /**
48298      * @event focus
48299      * @hide
48300      */
48301     /**
48302      * @event specialkey
48303      * @hide
48304      */
48305     /**
48306      * @cfg {String} fieldClass @hide
48307      */
48308     /**
48309      * @cfg {String} focusClass @hide
48310      */
48311     /**
48312      * @cfg {String} autoCreate @hide
48313      */
48314     /**
48315      * @cfg {String} inputType @hide
48316      */
48317     /**
48318      * @cfg {String} invalidClass @hide
48319      */
48320     /**
48321      * @cfg {String} invalidText @hide
48322      */
48323     /**
48324      * @cfg {String} msgFx @hide
48325      */
48326     /**
48327      * @cfg {String} validateOnBlur @hide
48328      */
48329 });
48330  
48331     // <script type="text/javascript">
48332 /*
48333  * Based on
48334  * Ext JS Library 1.1.1
48335  * Copyright(c) 2006-2007, Ext JS, LLC.
48336  *  
48337  
48338  */
48339
48340 /**
48341  * @class Roo.form.HtmlEditorToolbar1
48342  * Basic Toolbar
48343  * 
48344  * Usage:
48345  *
48346  new Roo.form.HtmlEditor({
48347     ....
48348     toolbars : [
48349         new Roo.form.HtmlEditorToolbar1({
48350             disable : { fonts: 1 , format: 1, ..., ... , ...],
48351             btns : [ .... ]
48352         })
48353     }
48354      
48355  * 
48356  * @cfg {Object} disable List of elements to disable..
48357  * @cfg {Array} btns List of additional buttons.
48358  * 
48359  * 
48360  * NEEDS Extra CSS? 
48361  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48362  */
48363  
48364 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48365 {
48366     
48367     Roo.apply(this, config);
48368     
48369     // default disabled, based on 'good practice'..
48370     this.disable = this.disable || {};
48371     Roo.applyIf(this.disable, {
48372         fontSize : true,
48373         colors : true,
48374         specialElements : true
48375     });
48376     
48377     
48378     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48379     // dont call parent... till later.
48380 }
48381
48382 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48383     
48384     tb: false,
48385     
48386     rendered: false,
48387     
48388     editor : false,
48389     editorcore : false,
48390     /**
48391      * @cfg {Object} disable  List of toolbar elements to disable
48392          
48393      */
48394     disable : false,
48395     
48396     
48397      /**
48398      * @cfg {String} createLinkText The default text for the create link prompt
48399      */
48400     createLinkText : 'Please enter the URL for the link:',
48401     /**
48402      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48403      */
48404     defaultLinkValue : 'http:/'+'/',
48405    
48406     
48407       /**
48408      * @cfg {Array} fontFamilies An array of available font families
48409      */
48410     fontFamilies : [
48411         'Arial',
48412         'Courier New',
48413         'Tahoma',
48414         'Times New Roman',
48415         'Verdana'
48416     ],
48417     
48418     specialChars : [
48419            "&#169;",
48420           "&#174;",     
48421           "&#8482;",    
48422           "&#163;" ,    
48423          // "&#8212;",    
48424           "&#8230;",    
48425           "&#247;" ,    
48426         //  "&#225;" ,     ?? a acute?
48427            "&#8364;"    , //Euro
48428        //   "&#8220;"    ,
48429         //  "&#8221;"    ,
48430         //  "&#8226;"    ,
48431           "&#176;"  //   , // degrees
48432
48433          // "&#233;"     , // e ecute
48434          // "&#250;"     , // u ecute?
48435     ],
48436     
48437     specialElements : [
48438         {
48439             text: "Insert Table",
48440             xtype: 'MenuItem',
48441             xns : Roo.Menu,
48442             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48443                 
48444         },
48445         {    
48446             text: "Insert Image",
48447             xtype: 'MenuItem',
48448             xns : Roo.Menu,
48449             ihtml : '<img src="about:blank"/>'
48450             
48451         }
48452         
48453          
48454     ],
48455     
48456     
48457     inputElements : [ 
48458             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48459             "input:submit", "input:button", "select", "textarea", "label" ],
48460     formats : [
48461         ["p"] ,  
48462         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48463         ["pre"],[ "code"], 
48464         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48465         ['div'],['span'],
48466         ['sup'],['sub']
48467     ],
48468     
48469     cleanStyles : [
48470         "font-size"
48471     ],
48472      /**
48473      * @cfg {String} defaultFont default font to use.
48474      */
48475     defaultFont: 'tahoma',
48476    
48477     fontSelect : false,
48478     
48479     
48480     formatCombo : false,
48481     
48482     init : function(editor)
48483     {
48484         this.editor = editor;
48485         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48486         var editorcore = this.editorcore;
48487         
48488         var _t = this;
48489         
48490         var fid = editorcore.frameId;
48491         var etb = this;
48492         function btn(id, toggle, handler){
48493             var xid = fid + '-'+ id ;
48494             return {
48495                 id : xid,
48496                 cmd : id,
48497                 cls : 'x-btn-icon x-edit-'+id,
48498                 enableToggle:toggle !== false,
48499                 scope: _t, // was editor...
48500                 handler:handler||_t.relayBtnCmd,
48501                 clickEvent:'mousedown',
48502                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48503                 tabIndex:-1
48504             };
48505         }
48506         
48507         
48508         
48509         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48510         this.tb = tb;
48511          // stop form submits
48512         tb.el.on('click', function(e){
48513             e.preventDefault(); // what does this do?
48514         });
48515
48516         if(!this.disable.font) { // && !Roo.isSafari){
48517             /* why no safari for fonts 
48518             editor.fontSelect = tb.el.createChild({
48519                 tag:'select',
48520                 tabIndex: -1,
48521                 cls:'x-font-select',
48522                 html: this.createFontOptions()
48523             });
48524             
48525             editor.fontSelect.on('change', function(){
48526                 var font = editor.fontSelect.dom.value;
48527                 editor.relayCmd('fontname', font);
48528                 editor.deferFocus();
48529             }, editor);
48530             
48531             tb.add(
48532                 editor.fontSelect.dom,
48533                 '-'
48534             );
48535             */
48536             
48537         };
48538         if(!this.disable.formats){
48539             this.formatCombo = new Roo.form.ComboBox({
48540                 store: new Roo.data.SimpleStore({
48541                     id : 'tag',
48542                     fields: ['tag'],
48543                     data : this.formats // from states.js
48544                 }),
48545                 blockFocus : true,
48546                 name : '',
48547                 //autoCreate : {tag: "div",  size: "20"},
48548                 displayField:'tag',
48549                 typeAhead: false,
48550                 mode: 'local',
48551                 editable : false,
48552                 triggerAction: 'all',
48553                 emptyText:'Add tag',
48554                 selectOnFocus:true,
48555                 width:135,
48556                 listeners : {
48557                     'select': function(c, r, i) {
48558                         editorcore.insertTag(r.get('tag'));
48559                         editor.focus();
48560                     }
48561                 }
48562
48563             });
48564             tb.addField(this.formatCombo);
48565             
48566         }
48567         
48568         if(!this.disable.format){
48569             tb.add(
48570                 btn('bold'),
48571                 btn('italic'),
48572                 btn('underline'),
48573                 btn('strikethrough')
48574             );
48575         };
48576         if(!this.disable.fontSize){
48577             tb.add(
48578                 '-',
48579                 
48580                 
48581                 btn('increasefontsize', false, editorcore.adjustFont),
48582                 btn('decreasefontsize', false, editorcore.adjustFont)
48583             );
48584         };
48585         
48586         
48587         if(!this.disable.colors){
48588             tb.add(
48589                 '-', {
48590                     id:editorcore.frameId +'-forecolor',
48591                     cls:'x-btn-icon x-edit-forecolor',
48592                     clickEvent:'mousedown',
48593                     tooltip: this.buttonTips['forecolor'] || undefined,
48594                     tabIndex:-1,
48595                     menu : new Roo.menu.ColorMenu({
48596                         allowReselect: true,
48597                         focus: Roo.emptyFn,
48598                         value:'000000',
48599                         plain:true,
48600                         selectHandler: function(cp, color){
48601                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48602                             editor.deferFocus();
48603                         },
48604                         scope: editorcore,
48605                         clickEvent:'mousedown'
48606                     })
48607                 }, {
48608                     id:editorcore.frameId +'backcolor',
48609                     cls:'x-btn-icon x-edit-backcolor',
48610                     clickEvent:'mousedown',
48611                     tooltip: this.buttonTips['backcolor'] || undefined,
48612                     tabIndex:-1,
48613                     menu : new Roo.menu.ColorMenu({
48614                         focus: Roo.emptyFn,
48615                         value:'FFFFFF',
48616                         plain:true,
48617                         allowReselect: true,
48618                         selectHandler: function(cp, color){
48619                             if(Roo.isGecko){
48620                                 editorcore.execCmd('useCSS', false);
48621                                 editorcore.execCmd('hilitecolor', color);
48622                                 editorcore.execCmd('useCSS', true);
48623                                 editor.deferFocus();
48624                             }else{
48625                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48626                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48627                                 editor.deferFocus();
48628                             }
48629                         },
48630                         scope:editorcore,
48631                         clickEvent:'mousedown'
48632                     })
48633                 }
48634             );
48635         };
48636         // now add all the items...
48637         
48638
48639         if(!this.disable.alignments){
48640             tb.add(
48641                 '-',
48642                 btn('justifyleft'),
48643                 btn('justifycenter'),
48644                 btn('justifyright')
48645             );
48646         };
48647
48648         //if(!Roo.isSafari){
48649             if(!this.disable.links){
48650                 tb.add(
48651                     '-',
48652                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48653                 );
48654             };
48655
48656             if(!this.disable.lists){
48657                 tb.add(
48658                     '-',
48659                     btn('insertorderedlist'),
48660                     btn('insertunorderedlist')
48661                 );
48662             }
48663             if(!this.disable.sourceEdit){
48664                 tb.add(
48665                     '-',
48666                     btn('sourceedit', true, function(btn){
48667                         this.toggleSourceEdit(btn.pressed);
48668                     })
48669                 );
48670             }
48671         //}
48672         
48673         var smenu = { };
48674         // special menu.. - needs to be tidied up..
48675         if (!this.disable.special) {
48676             smenu = {
48677                 text: "&#169;",
48678                 cls: 'x-edit-none',
48679                 
48680                 menu : {
48681                     items : []
48682                 }
48683             };
48684             for (var i =0; i < this.specialChars.length; i++) {
48685                 smenu.menu.items.push({
48686                     
48687                     html: this.specialChars[i],
48688                     handler: function(a,b) {
48689                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48690                         //editor.insertAtCursor(a.html);
48691                         
48692                     },
48693                     tabIndex:-1
48694                 });
48695             }
48696             
48697             
48698             tb.add(smenu);
48699             
48700             
48701         }
48702         
48703         var cmenu = { };
48704         if (!this.disable.cleanStyles) {
48705             cmenu = {
48706                 cls: 'x-btn-icon x-btn-clear',
48707                 
48708                 menu : {
48709                     items : []
48710                 }
48711             };
48712             for (var i =0; i < this.cleanStyles.length; i++) {
48713                 cmenu.menu.items.push({
48714                     actiontype : this.cleanStyles[i],
48715                     html: 'Remove ' + this.cleanStyles[i],
48716                     handler: function(a,b) {
48717 //                        Roo.log(a);
48718 //                        Roo.log(b);
48719                         var c = Roo.get(editorcore.doc.body);
48720                         c.select('[style]').each(function(s) {
48721                             s.dom.style.removeProperty(a.actiontype);
48722                         });
48723                         editorcore.syncValue();
48724                     },
48725                     tabIndex:-1
48726                 });
48727             }
48728             cmenu.menu.items.push({
48729                 actiontype : 'tablewidths',
48730                 html: 'Remove Table Widths',
48731                 handler: function(a,b) {
48732                     editorcore.cleanTableWidths();
48733                     editorcore.syncValue();
48734                 },
48735                 tabIndex:-1
48736             });
48737             cmenu.menu.items.push({
48738                 actiontype : 'word',
48739                 html: 'Remove MS Word Formating',
48740                 handler: function(a,b) {
48741                     editorcore.cleanWord();
48742                     editorcore.syncValue();
48743                 },
48744                 tabIndex:-1
48745             });
48746             
48747             cmenu.menu.items.push({
48748                 actiontype : 'all',
48749                 html: 'Remove All Styles',
48750                 handler: function(a,b) {
48751                     
48752                     var c = Roo.get(editorcore.doc.body);
48753                     c.select('[style]').each(function(s) {
48754                         s.dom.removeAttribute('style');
48755                     });
48756                     editorcore.syncValue();
48757                 },
48758                 tabIndex:-1
48759             });
48760             
48761             cmenu.menu.items.push({
48762                 actiontype : 'all',
48763                 html: 'Remove All CSS Classes',
48764                 handler: function(a,b) {
48765                     
48766                     var c = Roo.get(editorcore.doc.body);
48767                     c.select('[class]').each(function(s) {
48768                         s.dom.removeAttribute('class');
48769                     });
48770                     editorcore.cleanWord();
48771                     editorcore.syncValue();
48772                 },
48773                 tabIndex:-1
48774             });
48775             
48776              cmenu.menu.items.push({
48777                 actiontype : 'tidy',
48778                 html: 'Tidy HTML Source',
48779                 handler: function(a,b) {
48780                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48781                     editorcore.syncValue();
48782                 },
48783                 tabIndex:-1
48784             });
48785             
48786             
48787             tb.add(cmenu);
48788         }
48789          
48790         if (!this.disable.specialElements) {
48791             var semenu = {
48792                 text: "Other;",
48793                 cls: 'x-edit-none',
48794                 menu : {
48795                     items : []
48796                 }
48797             };
48798             for (var i =0; i < this.specialElements.length; i++) {
48799                 semenu.menu.items.push(
48800                     Roo.apply({ 
48801                         handler: function(a,b) {
48802                             editor.insertAtCursor(this.ihtml);
48803                         }
48804                     }, this.specialElements[i])
48805                 );
48806                     
48807             }
48808             
48809             tb.add(semenu);
48810             
48811             
48812         }
48813          
48814         
48815         if (this.btns) {
48816             for(var i =0; i< this.btns.length;i++) {
48817                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
48818                 b.cls =  'x-edit-none';
48819                 
48820                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48821                     b.cls += ' x-init-enable';
48822                 }
48823                 
48824                 b.scope = editorcore;
48825                 tb.add(b);
48826             }
48827         
48828         }
48829         
48830         
48831         
48832         // disable everything...
48833         
48834         this.tb.items.each(function(item){
48835             
48836            if(
48837                 item.id != editorcore.frameId+ '-sourceedit' && 
48838                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48839             ){
48840                 
48841                 item.disable();
48842             }
48843         });
48844         this.rendered = true;
48845         
48846         // the all the btns;
48847         editor.on('editorevent', this.updateToolbar, this);
48848         // other toolbars need to implement this..
48849         //editor.on('editmodechange', this.updateToolbar, this);
48850     },
48851     
48852     
48853     relayBtnCmd : function(btn) {
48854         this.editorcore.relayCmd(btn.cmd);
48855     },
48856     // private used internally
48857     createLink : function(){
48858         Roo.log("create link?");
48859         var url = prompt(this.createLinkText, this.defaultLinkValue);
48860         if(url && url != 'http:/'+'/'){
48861             this.editorcore.relayCmd('createlink', url);
48862         }
48863     },
48864
48865     
48866     /**
48867      * Protected method that will not generally be called directly. It triggers
48868      * a toolbar update by reading the markup state of the current selection in the editor.
48869      */
48870     updateToolbar: function(){
48871
48872         if(!this.editorcore.activated){
48873             this.editor.onFirstFocus();
48874             return;
48875         }
48876
48877         var btns = this.tb.items.map, 
48878             doc = this.editorcore.doc,
48879             frameId = this.editorcore.frameId;
48880
48881         if(!this.disable.font && !Roo.isSafari){
48882             /*
48883             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48884             if(name != this.fontSelect.dom.value){
48885                 this.fontSelect.dom.value = name;
48886             }
48887             */
48888         }
48889         if(!this.disable.format){
48890             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48891             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48892             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48893             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48894         }
48895         if(!this.disable.alignments){
48896             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48897             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48898             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48899         }
48900         if(!Roo.isSafari && !this.disable.lists){
48901             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48902             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48903         }
48904         
48905         var ans = this.editorcore.getAllAncestors();
48906         if (this.formatCombo) {
48907             
48908             
48909             var store = this.formatCombo.store;
48910             this.formatCombo.setValue("");
48911             for (var i =0; i < ans.length;i++) {
48912                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48913                     // select it..
48914                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48915                     break;
48916                 }
48917             }
48918         }
48919         
48920         
48921         
48922         // hides menus... - so this cant be on a menu...
48923         Roo.menu.MenuMgr.hideAll();
48924
48925         //this.editorsyncValue();
48926     },
48927    
48928     
48929     createFontOptions : function(){
48930         var buf = [], fs = this.fontFamilies, ff, lc;
48931         
48932         
48933         
48934         for(var i = 0, len = fs.length; i< len; i++){
48935             ff = fs[i];
48936             lc = ff.toLowerCase();
48937             buf.push(
48938                 '<option value="',lc,'" style="font-family:',ff,';"',
48939                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48940                     ff,
48941                 '</option>'
48942             );
48943         }
48944         return buf.join('');
48945     },
48946     
48947     toggleSourceEdit : function(sourceEditMode){
48948         
48949         Roo.log("toolbar toogle");
48950         if(sourceEditMode === undefined){
48951             sourceEditMode = !this.sourceEditMode;
48952         }
48953         this.sourceEditMode = sourceEditMode === true;
48954         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48955         // just toggle the button?
48956         if(btn.pressed !== this.sourceEditMode){
48957             btn.toggle(this.sourceEditMode);
48958             return;
48959         }
48960         
48961         if(sourceEditMode){
48962             Roo.log("disabling buttons");
48963             this.tb.items.each(function(item){
48964                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48965                     item.disable();
48966                 }
48967             });
48968           
48969         }else{
48970             Roo.log("enabling buttons");
48971             if(this.editorcore.initialized){
48972                 this.tb.items.each(function(item){
48973                     item.enable();
48974                 });
48975             }
48976             
48977         }
48978         Roo.log("calling toggole on editor");
48979         // tell the editor that it's been pressed..
48980         this.editor.toggleSourceEdit(sourceEditMode);
48981        
48982     },
48983      /**
48984      * Object collection of toolbar tooltips for the buttons in the editor. The key
48985      * is the command id associated with that button and the value is a valid QuickTips object.
48986      * For example:
48987 <pre><code>
48988 {
48989     bold : {
48990         title: 'Bold (Ctrl+B)',
48991         text: 'Make the selected text bold.',
48992         cls: 'x-html-editor-tip'
48993     },
48994     italic : {
48995         title: 'Italic (Ctrl+I)',
48996         text: 'Make the selected text italic.',
48997         cls: 'x-html-editor-tip'
48998     },
48999     ...
49000 </code></pre>
49001     * @type Object
49002      */
49003     buttonTips : {
49004         bold : {
49005             title: 'Bold (Ctrl+B)',
49006             text: 'Make the selected text bold.',
49007             cls: 'x-html-editor-tip'
49008         },
49009         italic : {
49010             title: 'Italic (Ctrl+I)',
49011             text: 'Make the selected text italic.',
49012             cls: 'x-html-editor-tip'
49013         },
49014         underline : {
49015             title: 'Underline (Ctrl+U)',
49016             text: 'Underline the selected text.',
49017             cls: 'x-html-editor-tip'
49018         },
49019         strikethrough : {
49020             title: 'Strikethrough',
49021             text: 'Strikethrough the selected text.',
49022             cls: 'x-html-editor-tip'
49023         },
49024         increasefontsize : {
49025             title: 'Grow Text',
49026             text: 'Increase the font size.',
49027             cls: 'x-html-editor-tip'
49028         },
49029         decreasefontsize : {
49030             title: 'Shrink Text',
49031             text: 'Decrease the font size.',
49032             cls: 'x-html-editor-tip'
49033         },
49034         backcolor : {
49035             title: 'Text Highlight Color',
49036             text: 'Change the background color of the selected text.',
49037             cls: 'x-html-editor-tip'
49038         },
49039         forecolor : {
49040             title: 'Font Color',
49041             text: 'Change the color of the selected text.',
49042             cls: 'x-html-editor-tip'
49043         },
49044         justifyleft : {
49045             title: 'Align Text Left',
49046             text: 'Align text to the left.',
49047             cls: 'x-html-editor-tip'
49048         },
49049         justifycenter : {
49050             title: 'Center Text',
49051             text: 'Center text in the editor.',
49052             cls: 'x-html-editor-tip'
49053         },
49054         justifyright : {
49055             title: 'Align Text Right',
49056             text: 'Align text to the right.',
49057             cls: 'x-html-editor-tip'
49058         },
49059         insertunorderedlist : {
49060             title: 'Bullet List',
49061             text: 'Start a bulleted list.',
49062             cls: 'x-html-editor-tip'
49063         },
49064         insertorderedlist : {
49065             title: 'Numbered List',
49066             text: 'Start a numbered list.',
49067             cls: 'x-html-editor-tip'
49068         },
49069         createlink : {
49070             title: 'Hyperlink',
49071             text: 'Make the selected text a hyperlink.',
49072             cls: 'x-html-editor-tip'
49073         },
49074         sourceedit : {
49075             title: 'Source Edit',
49076             text: 'Switch to source editing mode.',
49077             cls: 'x-html-editor-tip'
49078         }
49079     },
49080     // private
49081     onDestroy : function(){
49082         if(this.rendered){
49083             
49084             this.tb.items.each(function(item){
49085                 if(item.menu){
49086                     item.menu.removeAll();
49087                     if(item.menu.el){
49088                         item.menu.el.destroy();
49089                     }
49090                 }
49091                 item.destroy();
49092             });
49093              
49094         }
49095     },
49096     onFirstFocus: function() {
49097         this.tb.items.each(function(item){
49098            item.enable();
49099         });
49100     }
49101 });
49102
49103
49104
49105
49106 // <script type="text/javascript">
49107 /*
49108  * Based on
49109  * Ext JS Library 1.1.1
49110  * Copyright(c) 2006-2007, Ext JS, LLC.
49111  *  
49112  
49113  */
49114
49115  
49116 /**
49117  * @class Roo.form.HtmlEditor.ToolbarContext
49118  * Context Toolbar
49119  * 
49120  * Usage:
49121  *
49122  new Roo.form.HtmlEditor({
49123     ....
49124     toolbars : [
49125         { xtype: 'ToolbarStandard', styles : {} }
49126         { xtype: 'ToolbarContext', disable : {} }
49127     ]
49128 })
49129
49130      
49131  * 
49132  * @config : {Object} disable List of elements to disable.. (not done yet.)
49133  * @config : {Object} styles  Map of styles available.
49134  * 
49135  */
49136
49137 Roo.form.HtmlEditor.ToolbarContext = function(config)
49138 {
49139     
49140     Roo.apply(this, config);
49141     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49142     // dont call parent... till later.
49143     this.styles = this.styles || {};
49144 }
49145
49146  
49147
49148 Roo.form.HtmlEditor.ToolbarContext.types = {
49149     'IMG' : [
49150         {
49151             name : 'width',
49152             title: "Width",
49153             width: 40
49154         },
49155         {
49156             name : 'height',
49157             title: "Height",
49158             width: 40
49159         },
49160         {
49161             name : 'align',
49162             title: "Align",
49163             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49164             width : 80
49165             
49166         },
49167         {
49168             name : 'border',
49169             title: "Border",
49170             width: 40
49171         },
49172         {
49173             name : 'alt',
49174             title: "Alt",
49175             width: 120
49176         },
49177         {
49178             name : 'src',
49179             title: "Src",
49180             width: 220
49181         }
49182         
49183     ],
49184     
49185     'FIGURE' : [
49186         {
49187             name : 'align',
49188             title: "Align",
49189             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49190             width : 80  
49191         }
49192     ],
49193     'A' : [
49194         {
49195             name : 'name',
49196             title: "Name",
49197             width: 50
49198         },
49199         {
49200             name : 'target',
49201             title: "Target",
49202             width: 120
49203         },
49204         {
49205             name : 'href',
49206             title: "Href",
49207             width: 220
49208         } // border?
49209         
49210     ],
49211     
49212     'INPUT' : [
49213         {
49214             name : 'name',
49215             title: "name",
49216             width: 120
49217         },
49218         {
49219             name : 'value',
49220             title: "Value",
49221             width: 120
49222         },
49223         {
49224             name : 'width',
49225             title: "Width",
49226             width: 40
49227         }
49228     ],
49229     'LABEL' : [
49230          {
49231             name : 'for',
49232             title: "For",
49233             width: 120
49234         }
49235     ],
49236     'TEXTAREA' : [
49237         {
49238             name : 'name',
49239             title: "name",
49240             width: 120
49241         },
49242         {
49243             name : 'rows',
49244             title: "Rows",
49245             width: 20
49246         },
49247         {
49248             name : 'cols',
49249             title: "Cols",
49250             width: 20
49251         }
49252     ],
49253     'SELECT' : [
49254         {
49255             name : 'name',
49256             title: "name",
49257             width: 120
49258         },
49259         {
49260             name : 'selectoptions',
49261             title: "Options",
49262             width: 200
49263         }
49264     ],
49265     
49266     // should we really allow this??
49267     // should this just be 
49268     'BODY' : [
49269         
49270         {
49271             name : 'title',
49272             title: "Title",
49273             width: 200,
49274             disabled : true
49275         }
49276     ],
49277  
49278     '*' : [
49279         // empty.
49280     ]
49281
49282 };
49283
49284 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49285 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49286
49287 Roo.form.HtmlEditor.ToolbarContext.options = {
49288         'font-family'  : [ 
49289                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49290                 [ 'Courier New', 'Courier New'],
49291                 [ 'Tahoma', 'Tahoma'],
49292                 [ 'Times New Roman,serif', 'Times'],
49293                 [ 'Verdana','Verdana' ]
49294         ]
49295 };
49296
49297 // fixme - these need to be configurable..
49298  
49299
49300 //Roo.form.HtmlEditor.ToolbarContext.types
49301
49302
49303 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49304     
49305     tb: false,
49306     
49307     rendered: false,
49308     
49309     editor : false,
49310     editorcore : false,
49311     /**
49312      * @cfg {Object} disable  List of toolbar elements to disable
49313          
49314      */
49315     disable : false,
49316     /**
49317      * @cfg {Object} styles List of styles 
49318      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49319      *
49320      * These must be defined in the page, so they get rendered correctly..
49321      * .headline { }
49322      * TD.underline { }
49323      * 
49324      */
49325     styles : false,
49326     
49327     options: false,
49328     
49329     toolbars : false,
49330     
49331     init : function(editor)
49332     {
49333         this.editor = editor;
49334         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49335         var editorcore = this.editorcore;
49336         
49337         var fid = editorcore.frameId;
49338         var etb = this;
49339         function btn(id, toggle, handler){
49340             var xid = fid + '-'+ id ;
49341             return {
49342                 id : xid,
49343                 cmd : id,
49344                 cls : 'x-btn-icon x-edit-'+id,
49345                 enableToggle:toggle !== false,
49346                 scope: editorcore, // was editor...
49347                 handler:handler||editorcore.relayBtnCmd,
49348                 clickEvent:'mousedown',
49349                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49350                 tabIndex:-1
49351             };
49352         }
49353         // create a new element.
49354         var wdiv = editor.wrap.createChild({
49355                 tag: 'div'
49356             }, editor.wrap.dom.firstChild.nextSibling, true);
49357         
49358         // can we do this more than once??
49359         
49360          // stop form submits
49361       
49362  
49363         // disable everything...
49364         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49365         this.toolbars = {};
49366            
49367         for (var i in  ty) {
49368           
49369             this.toolbars[i] = this.buildToolbar(ty[i],i);
49370         }
49371         this.tb = this.toolbars.BODY;
49372         this.tb.el.show();
49373         this.buildFooter();
49374         this.footer.show();
49375         editor.on('hide', function( ) { this.footer.hide() }, this);
49376         editor.on('show', function( ) { this.footer.show() }, this);
49377         
49378          
49379         this.rendered = true;
49380         
49381         // the all the btns;
49382         editor.on('editorevent', this.updateToolbar, this);
49383         // other toolbars need to implement this..
49384         //editor.on('editmodechange', this.updateToolbar, this);
49385     },
49386     
49387     
49388     
49389     /**
49390      * Protected method that will not generally be called directly. It triggers
49391      * a toolbar update by reading the markup state of the current selection in the editor.
49392      *
49393      * Note you can force an update by calling on('editorevent', scope, false)
49394      */
49395     updateToolbar: function(editor ,ev, sel)
49396     {
49397         
49398         if (ev) {
49399             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49400         }
49401         
49402         //Roo.log(ev);
49403         // capture mouse up - this is handy for selecting images..
49404         // perhaps should go somewhere else...
49405         if(!this.editorcore.activated){
49406              this.editor.onFirstFocus();
49407             return;
49408         }
49409         Roo.log(ev ? ev.target : 'NOTARGET');
49410         
49411         
49412         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49413         // selectNode - might want to handle IE?
49414         
49415         
49416         
49417         if (ev &&
49418             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49419             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49420             // they have click on an image...
49421             // let's see if we can change the selection...
49422             sel = ev.target;
49423             
49424             // this triggers looping?
49425             //this.editorcore.selectNode(sel);
49426              
49427         }  
49428         
49429       
49430         //var updateFooter = sel ? false : true; 
49431         
49432         
49433         var ans = this.editorcore.getAllAncestors();
49434         
49435         // pick
49436         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49437         
49438         if (!sel) { 
49439             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49440             sel = sel ? sel : this.editorcore.doc.body;
49441             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49442             
49443         }
49444         
49445         var tn = sel.tagName.toUpperCase();
49446         var lastSel = this.tb.selectedNode;
49447         this.tb.selectedNode = sel;
49448         var left_label = tn;
49449         
49450         // ok see if we are editing a block?
49451         var sel_el = Roo.get(sel);
49452         var db = false;
49453         // you are not actually selecting the block.
49454         if (sel && sel.hasAttribute('data-block')) {
49455             db = sel;
49456         } else if (sel && !sel.hasAttribute('contenteditable')) {
49457             db = sel_el.findParent('[data-block]');
49458             var cepar = sel_el.findParent('[contenteditable=true]');
49459             if (db && cepar && cepar.tagName != 'BODY') {
49460                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49461             }   
49462         }
49463         
49464         
49465         var block = false;
49466         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49467         if (db) {
49468             block = Roo.htmleditor.Block.factory(db);
49469             if (block) {
49470                 tn = 'BLOCK.' + db.getAttribute('data-block');
49471                 
49472                 //this.editorcore.selectNode(db);
49473                 if (typeof(this.toolbars[tn]) == 'undefined') {
49474                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49475                 }
49476                 this.toolbars[tn].selectedNode = db;
49477                 left_label = block.friendly_name;
49478                 ans = this.editorcore.getAllAncestors();
49479             }
49480             
49481                 
49482             
49483         }
49484         
49485         
49486         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49487             return; // no change?
49488         }
49489         
49490         
49491           
49492         this.tb.el.hide();
49493         ///console.log("show: " + tn);
49494         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49495         
49496         this.tb.el.show();
49497         // update name
49498         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49499         
49500         
49501         // update attributes
49502         if (block) {
49503              
49504             this.tb.fields.each(function(e) {
49505                 e.setValue(block[e.name]);
49506             });
49507             
49508             
49509         } else  if (this.tb.fields && this.tb.selectedNode) {
49510             this.tb.fields.each( function(e) {
49511                 if (e.stylename) {
49512                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49513                     return;
49514                 } 
49515                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49516             }, this);
49517             this.updateToolbarStyles(this.tb.selectedNode);  
49518         }
49519         
49520         
49521        
49522         Roo.menu.MenuMgr.hideAll();
49523
49524         
49525         
49526     
49527         // update the footer
49528         //
49529         this.updateFooter(ans);
49530              
49531     },
49532     
49533     updateToolbarStyles : function(sel)
49534     {
49535         var hasStyles = false;
49536         for(var i in this.styles) {
49537             hasStyles = true;
49538             break;
49539         }
49540         
49541         // update styles
49542         if (hasStyles && this.tb.hasStyles) { 
49543             var st = this.tb.fields.item(0);
49544             
49545             st.store.removeAll();
49546             var cn = sel.className.split(/\s+/);
49547             
49548             var avs = [];
49549             if (this.styles['*']) {
49550                 
49551                 Roo.each(this.styles['*'], function(v) {
49552                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49553                 });
49554             }
49555             if (this.styles[tn]) { 
49556                 Roo.each(this.styles[tn], function(v) {
49557                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49558                 });
49559             }
49560             
49561             st.store.loadData(avs);
49562             st.collapse();
49563             st.setValue(cn);
49564         }
49565     },
49566     
49567      
49568     updateFooter : function(ans)
49569     {
49570         var html = '';
49571         if (ans === false) {
49572             this.footDisp.dom.innerHTML = '';
49573             return;
49574         }
49575         
49576         this.footerEls = ans.reverse();
49577         Roo.each(this.footerEls, function(a,i) {
49578             if (!a) { return; }
49579             html += html.length ? ' &gt; '  :  '';
49580             
49581             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49582             
49583         });
49584        
49585         // 
49586         var sz = this.footDisp.up('td').getSize();
49587         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49588         this.footDisp.dom.style.marginLeft = '5px';
49589         
49590         this.footDisp.dom.style.overflow = 'hidden';
49591         
49592         this.footDisp.dom.innerHTML = html;
49593             
49594         
49595     },
49596    
49597        
49598     // private
49599     onDestroy : function(){
49600         if(this.rendered){
49601             
49602             this.tb.items.each(function(item){
49603                 if(item.menu){
49604                     item.menu.removeAll();
49605                     if(item.menu.el){
49606                         item.menu.el.destroy();
49607                     }
49608                 }
49609                 item.destroy();
49610             });
49611              
49612         }
49613     },
49614     onFirstFocus: function() {
49615         // need to do this for all the toolbars..
49616         this.tb.items.each(function(item){
49617            item.enable();
49618         });
49619     },
49620     buildToolbar: function(tlist, nm, friendly_name, block)
49621     {
49622         var editor = this.editor;
49623         var editorcore = this.editorcore;
49624          // create a new element.
49625         var wdiv = editor.wrap.createChild({
49626                 tag: 'div'
49627             }, editor.wrap.dom.firstChild.nextSibling, true);
49628         
49629        
49630         var tb = new Roo.Toolbar(wdiv);
49631         this.tb = tb;
49632         if (tlist === false && block) {
49633             tlist = block.contextMenu(this);
49634         }
49635         
49636         tb.hasStyles = false;
49637         tb.name = nm;
49638         
49639         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49640         
49641         var styles = Array.from(this.styles);
49642         
49643         
49644         // styles...
49645         if (styles && styles.length) {
49646             tb.hasStyles = true;
49647             // this needs a multi-select checkbox...
49648             tb.addField( new Roo.form.ComboBox({
49649                 store: new Roo.data.SimpleStore({
49650                     id : 'val',
49651                     fields: ['val', 'selected'],
49652                     data : [] 
49653                 }),
49654                 name : '-roo-edit-className',
49655                 attrname : 'className',
49656                 displayField: 'val',
49657                 typeAhead: false,
49658                 mode: 'local',
49659                 editable : false,
49660                 triggerAction: 'all',
49661                 emptyText:'Select Style',
49662                 selectOnFocus:true,
49663                 width: 130,
49664                 listeners : {
49665                     'select': function(c, r, i) {
49666                         // initial support only for on class per el..
49667                         tb.selectedNode.className =  r ? r.get('val') : '';
49668                         editorcore.syncValue();
49669                     }
49670                 }
49671     
49672             }));
49673         }
49674         
49675         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49676         
49677         
49678         for (var i = 0; i < tlist.length; i++) {
49679             
49680             // newer versions will use xtype cfg to create menus.
49681             if (typeof(tlist[i].xtype) != 'undefined') {
49682                 
49683                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49684                 
49685                 
49686                 continue;
49687             }
49688             
49689             var item = tlist[i];
49690             tb.add(item.title + ":&nbsp;");
49691             
49692             
49693             //optname == used so you can configure the options available..
49694             var opts = item.opts ? item.opts : false;
49695             if (item.optname) { // use the b
49696                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49697            
49698             }
49699             
49700             if (opts) {
49701                 // opts == pulldown..
49702                 tb.addField( new Roo.form.ComboBox({
49703                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49704                         id : 'val',
49705                         fields: ['val', 'display'],
49706                         data : opts  
49707                     }),
49708                     name : '-roo-edit-' + tlist[i].name,
49709                     
49710                     attrname : tlist[i].name,
49711                     stylename : item.style ? item.style : false,
49712                     
49713                     displayField: item.displayField ? item.displayField : 'val',
49714                     valueField :  'val',
49715                     typeAhead: false,
49716                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
49717                     editable : false,
49718                     triggerAction: 'all',
49719                     emptyText:'Select',
49720                     selectOnFocus:true,
49721                     width: item.width ? item.width  : 130,
49722                     listeners : {
49723                         'select': function(c, r, i) {
49724                             if (tb.selectedNode.hasAttribute('data-block')) {
49725                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49726                                 b[c.attrname] = r.get('val');
49727                                 b.updateElement(tb.selectedNode);
49728                                 editorcore.syncValue();
49729                                 return;
49730                             }
49731                             
49732                             if (c.stylename) {
49733                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49734                                 editorcore.syncValue();
49735                                 return;
49736                             }
49737                             if (r === false) {
49738                                 tb.selectedNode.removeAttribute(c.attrname);
49739                                 editorcore.syncValue();
49740                                 return;
49741                             }
49742                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49743                             editorcore.syncValue();
49744                         }
49745                     }
49746
49747                 }));
49748                 continue;
49749                     
49750                  
49751                 /*
49752                 tb.addField( new Roo.form.TextField({
49753                     name: i,
49754                     width: 100,
49755                     //allowBlank:false,
49756                     value: ''
49757                 }));
49758                 continue;
49759                 */
49760             }
49761             tb.addField( new Roo.form.TextField({
49762                 name: '-roo-edit-' + tlist[i].name,
49763                 attrname : tlist[i].name,
49764                 
49765                 width: item.width,
49766                 //allowBlank:true,
49767                 value: '',
49768                 listeners: {
49769                     'change' : function(f, nv, ov) {
49770                         
49771                         if (tb.selectedNode.hasAttribute('data-block')) {
49772                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49773                             b[f.attrname] = nv;
49774                             b.updateElement(tb.selectedNode);
49775                             editorcore.syncValue();
49776                             return;
49777                         }
49778                         
49779                         tb.selectedNode.setAttribute(f.attrname, nv);
49780                         editorcore.syncValue();
49781                     }
49782                 }
49783             }));
49784              
49785         }
49786         
49787         var _this = this;
49788         
49789         if(nm == 'BODY'){
49790             tb.addSeparator();
49791         
49792             tb.addButton( {
49793                 text: 'Stylesheets',
49794
49795                 listeners : {
49796                     click : function ()
49797                     {
49798                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49799                     }
49800                 }
49801             });
49802         }
49803         
49804         tb.addFill();
49805         tb.addButton({
49806             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49807     
49808             listeners : {
49809                 click : function ()
49810                 {
49811                     var sn = tb.selectedNode;
49812                     if (block) {
49813                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removalNode();
49814                         
49815                     }
49816                     if (!sn) {
49817                         return;
49818                     }
49819                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49820                     if (sn.hasAttribute('data-block')) {
49821                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49822                         sn.parentNode.removeChild(sn);
49823                         
49824                     } else if (sn && sn.tagName != 'BODY') {
49825                         // remove and keep parents.
49826                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49827                         a.removeTag(sn);
49828                     }
49829                     
49830                     
49831                     var range = editorcore.createRange();
49832         
49833                     range.setStart(stn,0);
49834                     range.setEnd(stn,0); 
49835                     var selection = editorcore.getSelection();
49836                     selection.removeAllRanges();
49837                     selection.addRange(range);
49838                     
49839                     
49840                     //_this.updateToolbar(null, null, pn);
49841                     _this.updateToolbar(null, null, null);
49842                     _this.updateFooter(false);
49843                     
49844                 }
49845             }
49846             
49847                     
49848                 
49849             
49850         });
49851         
49852         
49853         tb.el.on('click', function(e){
49854             e.preventDefault(); // what does this do?
49855         });
49856         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49857         tb.el.hide();
49858         
49859         // dont need to disable them... as they will get hidden
49860         return tb;
49861          
49862         
49863     },
49864     buildFooter : function()
49865     {
49866         
49867         var fel = this.editor.wrap.createChild();
49868         this.footer = new Roo.Toolbar(fel);
49869         // toolbar has scrolly on left / right?
49870         var footDisp= new Roo.Toolbar.Fill();
49871         var _t = this;
49872         this.footer.add(
49873             {
49874                 text : '&lt;',
49875                 xtype: 'Button',
49876                 handler : function() {
49877                     _t.footDisp.scrollTo('left',0,true)
49878                 }
49879             }
49880         );
49881         this.footer.add( footDisp );
49882         this.footer.add( 
49883             {
49884                 text : '&gt;',
49885                 xtype: 'Button',
49886                 handler : function() {
49887                     // no animation..
49888                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49889                 }
49890             }
49891         );
49892         var fel = Roo.get(footDisp.el);
49893         fel.addClass('x-editor-context');
49894         this.footDispWrap = fel; 
49895         this.footDispWrap.overflow  = 'hidden';
49896         
49897         this.footDisp = fel.createChild();
49898         this.footDispWrap.on('click', this.onContextClick, this)
49899         
49900         
49901     },
49902     // when the footer contect changes
49903     onContextClick : function (ev,dom)
49904     {
49905         ev.preventDefault();
49906         var  cn = dom.className;
49907         //Roo.log(cn);
49908         if (!cn.match(/x-ed-loc-/)) {
49909             return;
49910         }
49911         var n = cn.split('-').pop();
49912         var ans = this.footerEls;
49913         var sel = ans[n];
49914         
49915          // pick
49916         var range = this.editorcore.createRange();
49917         
49918         range.selectNodeContents(sel);
49919         //range.selectNode(sel);
49920         
49921         
49922         var selection = this.editorcore.getSelection();
49923         selection.removeAllRanges();
49924         selection.addRange(range);
49925         
49926         
49927         
49928         this.updateToolbar(null, null, sel);
49929         
49930         
49931     }
49932     
49933     
49934     
49935     
49936     
49937 });
49938
49939
49940
49941
49942
49943 /*
49944  * Based on:
49945  * Ext JS Library 1.1.1
49946  * Copyright(c) 2006-2007, Ext JS, LLC.
49947  *
49948  * Originally Released Under LGPL - original licence link has changed is not relivant.
49949  *
49950  * Fork - LGPL
49951  * <script type="text/javascript">
49952  */
49953  
49954 /**
49955  * @class Roo.form.BasicForm
49956  * @extends Roo.util.Observable
49957  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49958  * @constructor
49959  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49960  * @param {Object} config Configuration options
49961  */
49962 Roo.form.BasicForm = function(el, config){
49963     this.allItems = [];
49964     this.childForms = [];
49965     Roo.apply(this, config);
49966     /*
49967      * The Roo.form.Field items in this form.
49968      * @type MixedCollection
49969      */
49970      
49971      
49972     this.items = new Roo.util.MixedCollection(false, function(o){
49973         return o.id || (o.id = Roo.id());
49974     });
49975     this.addEvents({
49976         /**
49977          * @event beforeaction
49978          * Fires before any action is performed. Return false to cancel the action.
49979          * @param {Form} this
49980          * @param {Action} action The action to be performed
49981          */
49982         beforeaction: true,
49983         /**
49984          * @event actionfailed
49985          * Fires when an action fails.
49986          * @param {Form} this
49987          * @param {Action} action The action that failed
49988          */
49989         actionfailed : true,
49990         /**
49991          * @event actioncomplete
49992          * Fires when an action is completed.
49993          * @param {Form} this
49994          * @param {Action} action The action that completed
49995          */
49996         actioncomplete : true
49997     });
49998     if(el){
49999         this.initEl(el);
50000     }
50001     Roo.form.BasicForm.superclass.constructor.call(this);
50002     
50003     Roo.form.BasicForm.popover.apply();
50004 };
50005
50006 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50007     /**
50008      * @cfg {String} method
50009      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50010      */
50011     /**
50012      * @cfg {DataReader} reader
50013      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50014      * This is optional as there is built-in support for processing JSON.
50015      */
50016     /**
50017      * @cfg {DataReader} errorReader
50018      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50019      * This is completely optional as there is built-in support for processing JSON.
50020      */
50021     /**
50022      * @cfg {String} url
50023      * The URL to use for form actions if one isn't supplied in the action options.
50024      */
50025     /**
50026      * @cfg {Boolean} fileUpload
50027      * Set to true if this form is a file upload.
50028      */
50029      
50030     /**
50031      * @cfg {Object} baseParams
50032      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50033      */
50034      /**
50035      
50036     /**
50037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50038      */
50039     timeout: 30,
50040
50041     // private
50042     activeAction : null,
50043
50044     /**
50045      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50046      * or setValues() data instead of when the form was first created.
50047      */
50048     trackResetOnLoad : false,
50049     
50050     
50051     /**
50052      * childForms - used for multi-tab forms
50053      * @type {Array}
50054      */
50055     childForms : false,
50056     
50057     /**
50058      * allItems - full list of fields.
50059      * @type {Array}
50060      */
50061     allItems : false,
50062     
50063     /**
50064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50065      * element by passing it or its id or mask the form itself by passing in true.
50066      * @type Mixed
50067      */
50068     waitMsgTarget : false,
50069     
50070     /**
50071      * @type Boolean
50072      */
50073     disableMask : false,
50074     
50075     /**
50076      * @cfg {Boolean} errorMask (true|false) default false
50077      */
50078     errorMask : false,
50079     
50080     /**
50081      * @cfg {Number} maskOffset Default 100
50082      */
50083     maskOffset : 100,
50084
50085     // private
50086     initEl : function(el){
50087         this.el = Roo.get(el);
50088         this.id = this.el.id || Roo.id();
50089         this.el.on('submit', this.onSubmit, this);
50090         this.el.addClass('x-form');
50091     },
50092
50093     // private
50094     onSubmit : function(e){
50095         e.stopEvent();
50096     },
50097
50098     /**
50099      * Returns true if client-side validation on the form is successful.
50100      * @return Boolean
50101      */
50102     isValid : function(){
50103         var valid = true;
50104         var target = false;
50105         this.items.each(function(f){
50106             if(f.validate()){
50107                 return;
50108             }
50109             
50110             valid = false;
50111                 
50112             if(!target && f.el.isVisible(true)){
50113                 target = f;
50114             }
50115         });
50116         
50117         if(this.errorMask && !valid){
50118             Roo.form.BasicForm.popover.mask(this, target);
50119         }
50120         
50121         return valid;
50122     },
50123     /**
50124      * Returns array of invalid form fields.
50125      * @return Array
50126      */
50127     
50128     invalidFields : function()
50129     {
50130         var ret = [];
50131         this.items.each(function(f){
50132             if(f.validate()){
50133                 return;
50134             }
50135             ret.push(f);
50136             
50137         });
50138         
50139         return ret;
50140     },
50141     
50142     
50143     /**
50144      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50145      * @return Boolean
50146      */
50147     isDirty : function(){
50148         var dirty = false;
50149         this.items.each(function(f){
50150            if(f.isDirty()){
50151                dirty = true;
50152                return false;
50153            }
50154         });
50155         return dirty;
50156     },
50157     
50158     /**
50159      * Returns true if any fields in this form have changed since their original load. (New version)
50160      * @return Boolean
50161      */
50162     
50163     hasChanged : function()
50164     {
50165         var dirty = false;
50166         this.items.each(function(f){
50167            if(f.hasChanged()){
50168                dirty = true;
50169                return false;
50170            }
50171         });
50172         return dirty;
50173         
50174     },
50175     /**
50176      * Resets all hasChanged to 'false' -
50177      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50178      * So hasChanged storage is only to be used for this purpose
50179      * @return Boolean
50180      */
50181     resetHasChanged : function()
50182     {
50183         this.items.each(function(f){
50184            f.resetHasChanged();
50185         });
50186         
50187     },
50188     
50189     
50190     /**
50191      * Performs a predefined action (submit or load) or custom actions you define on this form.
50192      * @param {String} actionName The name of the action type
50193      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50194      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50195      * accept other config options):
50196      * <pre>
50197 Property          Type             Description
50198 ----------------  ---------------  ----------------------------------------------------------------------------------
50199 url               String           The url for the action (defaults to the form's url)
50200 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50201 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50202 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50203                                    validate the form on the client (defaults to false)
50204      * </pre>
50205      * @return {BasicForm} this
50206      */
50207     doAction : function(action, options){
50208         if(typeof action == 'string'){
50209             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50210         }
50211         if(this.fireEvent('beforeaction', this, action) !== false){
50212             this.beforeAction(action);
50213             action.run.defer(100, action);
50214         }
50215         return this;
50216     },
50217
50218     /**
50219      * Shortcut to do a submit action.
50220      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50221      * @return {BasicForm} this
50222      */
50223     submit : function(options){
50224         this.doAction('submit', options);
50225         return this;
50226     },
50227
50228     /**
50229      * Shortcut to do a load action.
50230      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50231      * @return {BasicForm} this
50232      */
50233     load : function(options){
50234         this.doAction('load', options);
50235         return this;
50236     },
50237
50238     /**
50239      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50240      * @param {Record} record The record to edit
50241      * @return {BasicForm} this
50242      */
50243     updateRecord : function(record){
50244         record.beginEdit();
50245         var fs = record.fields;
50246         fs.each(function(f){
50247             var field = this.findField(f.name);
50248             if(field){
50249                 record.set(f.name, field.getValue());
50250             }
50251         }, this);
50252         record.endEdit();
50253         return this;
50254     },
50255
50256     /**
50257      * Loads an Roo.data.Record into this form.
50258      * @param {Record} record The record to load
50259      * @return {BasicForm} this
50260      */
50261     loadRecord : function(record){
50262         this.setValues(record.data);
50263         return this;
50264     },
50265
50266     // private
50267     beforeAction : function(action){
50268         var o = action.options;
50269         
50270         if(!this.disableMask) {
50271             if(this.waitMsgTarget === true){
50272                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50273             }else if(this.waitMsgTarget){
50274                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50275                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50276             }else {
50277                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50278             }
50279         }
50280         
50281          
50282     },
50283
50284     // private
50285     afterAction : function(action, success){
50286         this.activeAction = null;
50287         var o = action.options;
50288         
50289         if(!this.disableMask) {
50290             if(this.waitMsgTarget === true){
50291                 this.el.unmask();
50292             }else if(this.waitMsgTarget){
50293                 this.waitMsgTarget.unmask();
50294             }else{
50295                 Roo.MessageBox.updateProgress(1);
50296                 Roo.MessageBox.hide();
50297             }
50298         }
50299         
50300         if(success){
50301             if(o.reset){
50302                 this.reset();
50303             }
50304             Roo.callback(o.success, o.scope, [this, action]);
50305             this.fireEvent('actioncomplete', this, action);
50306             
50307         }else{
50308             
50309             // failure condition..
50310             // we have a scenario where updates need confirming.
50311             // eg. if a locking scenario exists..
50312             // we look for { errors : { needs_confirm : true }} in the response.
50313             if (
50314                 (typeof(action.result) != 'undefined')  &&
50315                 (typeof(action.result.errors) != 'undefined')  &&
50316                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50317            ){
50318                 var _t = this;
50319                 Roo.MessageBox.confirm(
50320                     "Change requires confirmation",
50321                     action.result.errorMsg,
50322                     function(r) {
50323                         if (r != 'yes') {
50324                             return;
50325                         }
50326                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50327                     }
50328                     
50329                 );
50330                 
50331                 
50332                 
50333                 return;
50334             }
50335             
50336             Roo.callback(o.failure, o.scope, [this, action]);
50337             // show an error message if no failed handler is set..
50338             if (!this.hasListener('actionfailed')) {
50339                 Roo.MessageBox.alert("Error",
50340                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50341                         action.result.errorMsg :
50342                         "Saving Failed, please check your entries or try again"
50343                 );
50344             }
50345             
50346             this.fireEvent('actionfailed', this, action);
50347         }
50348         
50349     },
50350
50351     /**
50352      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50353      * @param {String} id The value to search for
50354      * @return Field
50355      */
50356     findField : function(id){
50357         var field = this.items.get(id);
50358         if(!field){
50359             this.items.each(function(f){
50360                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50361                     field = f;
50362                     return false;
50363                 }
50364             });
50365         }
50366         return field || null;
50367     },
50368
50369     /**
50370      * Add a secondary form to this one, 
50371      * Used to provide tabbed forms. One form is primary, with hidden values 
50372      * which mirror the elements from the other forms.
50373      * 
50374      * @param {Roo.form.Form} form to add.
50375      * 
50376      */
50377     addForm : function(form)
50378     {
50379        
50380         if (this.childForms.indexOf(form) > -1) {
50381             // already added..
50382             return;
50383         }
50384         this.childForms.push(form);
50385         var n = '';
50386         Roo.each(form.allItems, function (fe) {
50387             
50388             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50389             if (this.findField(n)) { // already added..
50390                 return;
50391             }
50392             var add = new Roo.form.Hidden({
50393                 name : n
50394             });
50395             add.render(this.el);
50396             
50397             this.add( add );
50398         }, this);
50399         
50400     },
50401     /**
50402      * Mark fields in this form invalid in bulk.
50403      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50404      * @return {BasicForm} this
50405      */
50406     markInvalid : function(errors){
50407         if(errors instanceof Array){
50408             for(var i = 0, len = errors.length; i < len; i++){
50409                 var fieldError = errors[i];
50410                 var f = this.findField(fieldError.id);
50411                 if(f){
50412                     f.markInvalid(fieldError.msg);
50413                 }
50414             }
50415         }else{
50416             var field, id;
50417             for(id in errors){
50418                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50419                     field.markInvalid(errors[id]);
50420                 }
50421             }
50422         }
50423         Roo.each(this.childForms || [], function (f) {
50424             f.markInvalid(errors);
50425         });
50426         
50427         return this;
50428     },
50429
50430     /**
50431      * Set values for fields in this form in bulk.
50432      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50433      * @return {BasicForm} this
50434      */
50435     setValues : function(values){
50436         if(values instanceof Array){ // array of objects
50437             for(var i = 0, len = values.length; i < len; i++){
50438                 var v = values[i];
50439                 var f = this.findField(v.id);
50440                 if(f){
50441                     f.setValue(v.value);
50442                     if(this.trackResetOnLoad){
50443                         f.originalValue = f.getValue();
50444                     }
50445                 }
50446             }
50447         }else{ // object hash
50448             var field, id;
50449             for(id in values){
50450                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50451                     
50452                     if (field.setFromData && 
50453                         field.valueField && 
50454                         field.displayField &&
50455                         // combos' with local stores can 
50456                         // be queried via setValue()
50457                         // to set their value..
50458                         (field.store && !field.store.isLocal)
50459                         ) {
50460                         // it's a combo
50461                         var sd = { };
50462                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50463                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50464                         field.setFromData(sd);
50465                         
50466                     } else {
50467                         field.setValue(values[id]);
50468                     }
50469                     
50470                     
50471                     if(this.trackResetOnLoad){
50472                         field.originalValue = field.getValue();
50473                     }
50474                 }
50475             }
50476         }
50477         this.resetHasChanged();
50478         
50479         
50480         Roo.each(this.childForms || [], function (f) {
50481             f.setValues(values);
50482             f.resetHasChanged();
50483         });
50484                 
50485         return this;
50486     },
50487  
50488     /**
50489      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50490      * they are returned as an array.
50491      * @param {Boolean} asString
50492      * @return {Object}
50493      */
50494     getValues : function(asString)
50495     {
50496         if (this.childForms) {
50497             // copy values from the child forms
50498             Roo.each(this.childForms, function (f) {
50499                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50500             }, this);
50501         }
50502         
50503         // use formdata
50504         if (typeof(FormData) != 'undefined' && asString !== true) {
50505             // this relies on a 'recent' version of chrome apparently...
50506             try {
50507                 var fd = (new FormData(this.el.dom)).entries();
50508                 var ret = {};
50509                 var ent = fd.next();
50510                 while (!ent.done) {
50511                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50512                     ent = fd.next();
50513                 };
50514                 return ret;
50515             } catch(e) {
50516                 
50517             }
50518             
50519         }
50520         
50521         
50522         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50523         if(asString === true){
50524             return fs;
50525         }
50526         return Roo.urlDecode(fs);
50527     },
50528     
50529     /**
50530      * Returns the fields in this form as an object with key/value pairs. 
50531      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50532      * @return {Object}
50533      */
50534     getFieldValues : function(with_hidden)
50535     {
50536         if (this.childForms) {
50537             // copy values from the child forms
50538             // should this call getFieldValues - probably not as we do not currently copy
50539             // hidden fields when we generate..
50540             Roo.each(this.childForms, function (f) {
50541                 this.setValues(f.getFieldValues());
50542             }, this);
50543         }
50544         
50545         var ret = {};
50546         this.items.each(function(f){
50547             
50548             if (f.readOnly) {
50549                 return; // skip read only values.
50550             }
50551             
50552             if (!f.getName()) {
50553                 return;
50554             }
50555             var v = f.getValue();
50556             if (f.inputType =='radio') {
50557                 if (typeof(ret[f.getName()]) == 'undefined') {
50558                     ret[f.getName()] = ''; // empty..
50559                 }
50560                 
50561                 if (!f.el.dom.checked) {
50562                     return;
50563                     
50564                 }
50565                 v = f.el.dom.value;
50566                 
50567             }
50568             
50569             // not sure if this supported any more..
50570             if ((typeof(v) == 'object') && f.getRawValue) {
50571                 v = f.getRawValue() ; // dates..
50572             }
50573             // combo boxes where name != hiddenName...
50574             if (f.name != f.getName()) {
50575                 ret[f.name] = f.getRawValue();
50576             }
50577             ret[f.getName()] = v;
50578         });
50579         
50580         return ret;
50581     },
50582
50583     /**
50584      * Clears all invalid messages in this form.
50585      * @return {BasicForm} this
50586      */
50587     clearInvalid : function(){
50588         this.items.each(function(f){
50589            f.clearInvalid();
50590         });
50591         
50592         Roo.each(this.childForms || [], function (f) {
50593             f.clearInvalid();
50594         });
50595         
50596         
50597         return this;
50598     },
50599
50600     /**
50601      * Resets this form.
50602      * @return {BasicForm} this
50603      */
50604     reset : function(){
50605         this.items.each(function(f){
50606             f.reset();
50607         });
50608         
50609         Roo.each(this.childForms || [], function (f) {
50610             f.reset();
50611         });
50612         this.resetHasChanged();
50613         
50614         return this;
50615     },
50616
50617     /**
50618      * Add Roo.form components to this form.
50619      * @param {Field} field1
50620      * @param {Field} field2 (optional)
50621      * @param {Field} etc (optional)
50622      * @return {BasicForm} this
50623      */
50624     add : function(){
50625         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50626         return this;
50627     },
50628
50629
50630     /**
50631      * Removes a field from the items collection (does NOT remove its markup).
50632      * @param {Field} field
50633      * @return {BasicForm} this
50634      */
50635     remove : function(field){
50636         this.items.remove(field);
50637         return this;
50638     },
50639
50640     /**
50641      * Looks at the fields in this form, checks them for an id attribute,
50642      * and calls applyTo on the existing dom element with that id.
50643      * @return {BasicForm} this
50644      */
50645     render : function(){
50646         this.items.each(function(f){
50647             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50648                 f.applyTo(f.id);
50649             }
50650         });
50651         return this;
50652     },
50653
50654     /**
50655      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50656      * @param {Object} values
50657      * @return {BasicForm} this
50658      */
50659     applyToFields : function(o){
50660         this.items.each(function(f){
50661            Roo.apply(f, o);
50662         });
50663         return this;
50664     },
50665
50666     /**
50667      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50668      * @param {Object} values
50669      * @return {BasicForm} this
50670      */
50671     applyIfToFields : function(o){
50672         this.items.each(function(f){
50673            Roo.applyIf(f, o);
50674         });
50675         return this;
50676     }
50677 });
50678
50679 // back compat
50680 Roo.BasicForm = Roo.form.BasicForm;
50681
50682 Roo.apply(Roo.form.BasicForm, {
50683     
50684     popover : {
50685         
50686         padding : 5,
50687         
50688         isApplied : false,
50689         
50690         isMasked : false,
50691         
50692         form : false,
50693         
50694         target : false,
50695         
50696         intervalID : false,
50697         
50698         maskEl : false,
50699         
50700         apply : function()
50701         {
50702             if(this.isApplied){
50703                 return;
50704             }
50705             
50706             this.maskEl = {
50707                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50708                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50709                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50710                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50711             };
50712             
50713             this.maskEl.top.enableDisplayMode("block");
50714             this.maskEl.left.enableDisplayMode("block");
50715             this.maskEl.bottom.enableDisplayMode("block");
50716             this.maskEl.right.enableDisplayMode("block");
50717             
50718             Roo.get(document.body).on('click', function(){
50719                 this.unmask();
50720             }, this);
50721             
50722             Roo.get(document.body).on('touchstart', function(){
50723                 this.unmask();
50724             }, this);
50725             
50726             this.isApplied = true
50727         },
50728         
50729         mask : function(form, target)
50730         {
50731             this.form = form;
50732             
50733             this.target = target;
50734             
50735             if(!this.form.errorMask || !target.el){
50736                 return;
50737             }
50738             
50739             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50740             
50741             var ot = this.target.el.calcOffsetsTo(scrollable);
50742             
50743             var scrollTo = ot[1] - this.form.maskOffset;
50744             
50745             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50746             
50747             scrollable.scrollTo('top', scrollTo);
50748             
50749             var el = this.target.wrap || this.target.el;
50750             
50751             var box = el.getBox();
50752             
50753             this.maskEl.top.setStyle('position', 'absolute');
50754             this.maskEl.top.setStyle('z-index', 10000);
50755             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50756             this.maskEl.top.setLeft(0);
50757             this.maskEl.top.setTop(0);
50758             this.maskEl.top.show();
50759             
50760             this.maskEl.left.setStyle('position', 'absolute');
50761             this.maskEl.left.setStyle('z-index', 10000);
50762             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50763             this.maskEl.left.setLeft(0);
50764             this.maskEl.left.setTop(box.y - this.padding);
50765             this.maskEl.left.show();
50766
50767             this.maskEl.bottom.setStyle('position', 'absolute');
50768             this.maskEl.bottom.setStyle('z-index', 10000);
50769             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50770             this.maskEl.bottom.setLeft(0);
50771             this.maskEl.bottom.setTop(box.bottom + this.padding);
50772             this.maskEl.bottom.show();
50773
50774             this.maskEl.right.setStyle('position', 'absolute');
50775             this.maskEl.right.setStyle('z-index', 10000);
50776             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50777             this.maskEl.right.setLeft(box.right + this.padding);
50778             this.maskEl.right.setTop(box.y - this.padding);
50779             this.maskEl.right.show();
50780
50781             this.intervalID = window.setInterval(function() {
50782                 Roo.form.BasicForm.popover.unmask();
50783             }, 10000);
50784
50785             window.onwheel = function(){ return false;};
50786             
50787             (function(){ this.isMasked = true; }).defer(500, this);
50788             
50789         },
50790         
50791         unmask : function()
50792         {
50793             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50794                 return;
50795             }
50796             
50797             this.maskEl.top.setStyle('position', 'absolute');
50798             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50799             this.maskEl.top.hide();
50800
50801             this.maskEl.left.setStyle('position', 'absolute');
50802             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50803             this.maskEl.left.hide();
50804
50805             this.maskEl.bottom.setStyle('position', 'absolute');
50806             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50807             this.maskEl.bottom.hide();
50808
50809             this.maskEl.right.setStyle('position', 'absolute');
50810             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50811             this.maskEl.right.hide();
50812             
50813             window.onwheel = function(){ return true;};
50814             
50815             if(this.intervalID){
50816                 window.clearInterval(this.intervalID);
50817                 this.intervalID = false;
50818             }
50819             
50820             this.isMasked = false;
50821             
50822         }
50823         
50824     }
50825     
50826 });/*
50827  * Based on:
50828  * Ext JS Library 1.1.1
50829  * Copyright(c) 2006-2007, Ext JS, LLC.
50830  *
50831  * Originally Released Under LGPL - original licence link has changed is not relivant.
50832  *
50833  * Fork - LGPL
50834  * <script type="text/javascript">
50835  */
50836
50837 /**
50838  * @class Roo.form.Form
50839  * @extends Roo.form.BasicForm
50840  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50841  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50842  * @constructor
50843  * @param {Object} config Configuration options
50844  */
50845 Roo.form.Form = function(config){
50846     var xitems =  [];
50847     if (config.items) {
50848         xitems = config.items;
50849         delete config.items;
50850     }
50851    
50852     
50853     Roo.form.Form.superclass.constructor.call(this, null, config);
50854     this.url = this.url || this.action;
50855     if(!this.root){
50856         this.root = new Roo.form.Layout(Roo.applyIf({
50857             id: Roo.id()
50858         }, config));
50859     }
50860     this.active = this.root;
50861     /**
50862      * Array of all the buttons that have been added to this form via {@link addButton}
50863      * @type Array
50864      */
50865     this.buttons = [];
50866     this.allItems = [];
50867     this.addEvents({
50868         /**
50869          * @event clientvalidation
50870          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50871          * @param {Form} this
50872          * @param {Boolean} valid true if the form has passed client-side validation
50873          */
50874         clientvalidation: true,
50875         /**
50876          * @event rendered
50877          * Fires when the form is rendered
50878          * @param {Roo.form.Form} form
50879          */
50880         rendered : true
50881     });
50882     
50883     if (this.progressUrl) {
50884             // push a hidden field onto the list of fields..
50885             this.addxtype( {
50886                     xns: Roo.form, 
50887                     xtype : 'Hidden', 
50888                     name : 'UPLOAD_IDENTIFIER' 
50889             });
50890         }
50891         
50892     
50893     Roo.each(xitems, this.addxtype, this);
50894     
50895 };
50896
50897 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50898      /**
50899      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50900      */
50901     
50902     /**
50903      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50904      */
50905     /**
50906      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50907      */
50908     /**
50909      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50910      */
50911     buttonAlign:'center',
50912
50913     /**
50914      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50915      */
50916     minButtonWidth:75,
50917
50918     /**
50919      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50920      * This property cascades to child containers if not set.
50921      */
50922     labelAlign:'left',
50923
50924     /**
50925      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50926      * fires a looping event with that state. This is required to bind buttons to the valid
50927      * state using the config value formBind:true on the button.
50928      */
50929     monitorValid : false,
50930
50931     /**
50932      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50933      */
50934     monitorPoll : 200,
50935     
50936     /**
50937      * @cfg {String} progressUrl - Url to return progress data 
50938      */
50939     
50940     progressUrl : false,
50941     /**
50942      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50943      * sending a formdata with extra parameters - eg uploaded elements.
50944      */
50945     
50946     formData : false,
50947     
50948     /**
50949      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50950      * fields are added and the column is closed. If no fields are passed the column remains open
50951      * until end() is called.
50952      * @param {Object} config The config to pass to the column
50953      * @param {Field} field1 (optional)
50954      * @param {Field} field2 (optional)
50955      * @param {Field} etc (optional)
50956      * @return Column The column container object
50957      */
50958     column : function(c){
50959         var col = new Roo.form.Column(c);
50960         this.start(col);
50961         if(arguments.length > 1){ // duplicate code required because of Opera
50962             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50963             this.end();
50964         }
50965         return col;
50966     },
50967
50968     /**
50969      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50970      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50971      * until end() is called.
50972      * @param {Object} config The config to pass to the fieldset
50973      * @param {Field} field1 (optional)
50974      * @param {Field} field2 (optional)
50975      * @param {Field} etc (optional)
50976      * @return FieldSet The fieldset container object
50977      */
50978     fieldset : function(c){
50979         var fs = new Roo.form.FieldSet(c);
50980         this.start(fs);
50981         if(arguments.length > 1){ // duplicate code required because of Opera
50982             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50983             this.end();
50984         }
50985         return fs;
50986     },
50987
50988     /**
50989      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50990      * fields are added and the container is closed. If no fields are passed the container remains open
50991      * until end() is called.
50992      * @param {Object} config The config to pass to the Layout
50993      * @param {Field} field1 (optional)
50994      * @param {Field} field2 (optional)
50995      * @param {Field} etc (optional)
50996      * @return Layout The container object
50997      */
50998     container : function(c){
50999         var l = new Roo.form.Layout(c);
51000         this.start(l);
51001         if(arguments.length > 1){ // duplicate code required because of Opera
51002             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51003             this.end();
51004         }
51005         return l;
51006     },
51007
51008     /**
51009      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51010      * @param {Object} container A Roo.form.Layout or subclass of Layout
51011      * @return {Form} this
51012      */
51013     start : function(c){
51014         // cascade label info
51015         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51016         this.active.stack.push(c);
51017         c.ownerCt = this.active;
51018         this.active = c;
51019         return this;
51020     },
51021
51022     /**
51023      * Closes the current open container
51024      * @return {Form} this
51025      */
51026     end : function(){
51027         if(this.active == this.root){
51028             return this;
51029         }
51030         this.active = this.active.ownerCt;
51031         return this;
51032     },
51033
51034     /**
51035      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51036      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51037      * as the label of the field.
51038      * @param {Field} field1
51039      * @param {Field} field2 (optional)
51040      * @param {Field} etc. (optional)
51041      * @return {Form} this
51042      */
51043     add : function(){
51044         this.active.stack.push.apply(this.active.stack, arguments);
51045         this.allItems.push.apply(this.allItems,arguments);
51046         var r = [];
51047         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51048             if(a[i].isFormField){
51049                 r.push(a[i]);
51050             }
51051         }
51052         if(r.length > 0){
51053             Roo.form.Form.superclass.add.apply(this, r);
51054         }
51055         return this;
51056     },
51057     
51058
51059     
51060     
51061     
51062      /**
51063      * Find any element that has been added to a form, using it's ID or name
51064      * This can include framesets, columns etc. along with regular fields..
51065      * @param {String} id - id or name to find.
51066      
51067      * @return {Element} e - or false if nothing found.
51068      */
51069     findbyId : function(id)
51070     {
51071         var ret = false;
51072         if (!id) {
51073             return ret;
51074         }
51075         Roo.each(this.allItems, function(f){
51076             if (f.id == id || f.name == id ){
51077                 ret = f;
51078                 return false;
51079             }
51080         });
51081         return ret;
51082     },
51083
51084     
51085     
51086     /**
51087      * Render this form into the passed container. This should only be called once!
51088      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51089      * @return {Form} this
51090      */
51091     render : function(ct)
51092     {
51093         
51094         
51095         
51096         ct = Roo.get(ct);
51097         var o = this.autoCreate || {
51098             tag: 'form',
51099             method : this.method || 'POST',
51100             id : this.id || Roo.id()
51101         };
51102         this.initEl(ct.createChild(o));
51103
51104         this.root.render(this.el);
51105         
51106        
51107              
51108         this.items.each(function(f){
51109             f.render('x-form-el-'+f.id);
51110         });
51111
51112         if(this.buttons.length > 0){
51113             // tables are required to maintain order and for correct IE layout
51114             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51115                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51116                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51117             }}, null, true);
51118             var tr = tb.getElementsByTagName('tr')[0];
51119             for(var i = 0, len = this.buttons.length; i < len; i++) {
51120                 var b = this.buttons[i];
51121                 var td = document.createElement('td');
51122                 td.className = 'x-form-btn-td';
51123                 b.render(tr.appendChild(td));
51124             }
51125         }
51126         if(this.monitorValid){ // initialize after render
51127             this.startMonitoring();
51128         }
51129         this.fireEvent('rendered', this);
51130         return this;
51131     },
51132
51133     /**
51134      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51135      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51136      * object or a valid Roo.DomHelper element config
51137      * @param {Function} handler The function called when the button is clicked
51138      * @param {Object} scope (optional) The scope of the handler function
51139      * @return {Roo.Button}
51140      */
51141     addButton : function(config, handler, scope){
51142         var bc = {
51143             handler: handler,
51144             scope: scope,
51145             minWidth: this.minButtonWidth,
51146             hideParent:true
51147         };
51148         if(typeof config == "string"){
51149             bc.text = config;
51150         }else{
51151             Roo.apply(bc, config);
51152         }
51153         var btn = new Roo.Button(null, bc);
51154         this.buttons.push(btn);
51155         return btn;
51156     },
51157
51158      /**
51159      * Adds a series of form elements (using the xtype property as the factory method.
51160      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51161      * @param {Object} config 
51162      */
51163     
51164     addxtype : function()
51165     {
51166         var ar = Array.prototype.slice.call(arguments, 0);
51167         var ret = false;
51168         for(var i = 0; i < ar.length; i++) {
51169             if (!ar[i]) {
51170                 continue; // skip -- if this happends something invalid got sent, we 
51171                 // should ignore it, as basically that interface element will not show up
51172                 // and that should be pretty obvious!!
51173             }
51174             
51175             if (Roo.form[ar[i].xtype]) {
51176                 ar[i].form = this;
51177                 var fe = Roo.factory(ar[i], Roo.form);
51178                 if (!ret) {
51179                     ret = fe;
51180                 }
51181                 fe.form = this;
51182                 if (fe.store) {
51183                     fe.store.form = this;
51184                 }
51185                 if (fe.isLayout) {  
51186                          
51187                     this.start(fe);
51188                     this.allItems.push(fe);
51189                     if (fe.items && fe.addxtype) {
51190                         fe.addxtype.apply(fe, fe.items);
51191                         delete fe.items;
51192                     }
51193                      this.end();
51194                     continue;
51195                 }
51196                 
51197                 
51198                  
51199                 this.add(fe);
51200               //  console.log('adding ' + ar[i].xtype);
51201             }
51202             if (ar[i].xtype == 'Button') {  
51203                 //console.log('adding button');
51204                 //console.log(ar[i]);
51205                 this.addButton(ar[i]);
51206                 this.allItems.push(fe);
51207                 continue;
51208             }
51209             
51210             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51211                 alert('end is not supported on xtype any more, use items');
51212             //    this.end();
51213             //    //console.log('adding end');
51214             }
51215             
51216         }
51217         return ret;
51218     },
51219     
51220     /**
51221      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51222      * option "monitorValid"
51223      */
51224     startMonitoring : function(){
51225         if(!this.bound){
51226             this.bound = true;
51227             Roo.TaskMgr.start({
51228                 run : this.bindHandler,
51229                 interval : this.monitorPoll || 200,
51230                 scope: this
51231             });
51232         }
51233     },
51234
51235     /**
51236      * Stops monitoring of the valid state of this form
51237      */
51238     stopMonitoring : function(){
51239         this.bound = false;
51240     },
51241
51242     // private
51243     bindHandler : function(){
51244         if(!this.bound){
51245             return false; // stops binding
51246         }
51247         var valid = true;
51248         this.items.each(function(f){
51249             if(!f.isValid(true)){
51250                 valid = false;
51251                 return false;
51252             }
51253         });
51254         for(var i = 0, len = this.buttons.length; i < len; i++){
51255             var btn = this.buttons[i];
51256             if(btn.formBind === true && btn.disabled === valid){
51257                 btn.setDisabled(!valid);
51258             }
51259         }
51260         this.fireEvent('clientvalidation', this, valid);
51261     }
51262     
51263     
51264     
51265     
51266     
51267     
51268     
51269     
51270 });
51271
51272
51273 // back compat
51274 Roo.Form = Roo.form.Form;
51275 /*
51276  * Based on:
51277  * Ext JS Library 1.1.1
51278  * Copyright(c) 2006-2007, Ext JS, LLC.
51279  *
51280  * Originally Released Under LGPL - original licence link has changed is not relivant.
51281  *
51282  * Fork - LGPL
51283  * <script type="text/javascript">
51284  */
51285
51286 // as we use this in bootstrap.
51287 Roo.namespace('Roo.form');
51288  /**
51289  * @class Roo.form.Action
51290  * Internal Class used to handle form actions
51291  * @constructor
51292  * @param {Roo.form.BasicForm} el The form element or its id
51293  * @param {Object} config Configuration options
51294  */
51295
51296  
51297  
51298 // define the action interface
51299 Roo.form.Action = function(form, options){
51300     this.form = form;
51301     this.options = options || {};
51302 };
51303 /**
51304  * Client Validation Failed
51305  * @const 
51306  */
51307 Roo.form.Action.CLIENT_INVALID = 'client';
51308 /**
51309  * Server Validation Failed
51310  * @const 
51311  */
51312 Roo.form.Action.SERVER_INVALID = 'server';
51313  /**
51314  * Connect to Server Failed
51315  * @const 
51316  */
51317 Roo.form.Action.CONNECT_FAILURE = 'connect';
51318 /**
51319  * Reading Data from Server Failed
51320  * @const 
51321  */
51322 Roo.form.Action.LOAD_FAILURE = 'load';
51323
51324 Roo.form.Action.prototype = {
51325     type : 'default',
51326     failureType : undefined,
51327     response : undefined,
51328     result : undefined,
51329
51330     // interface method
51331     run : function(options){
51332
51333     },
51334
51335     // interface method
51336     success : function(response){
51337
51338     },
51339
51340     // interface method
51341     handleResponse : function(response){
51342
51343     },
51344
51345     // default connection failure
51346     failure : function(response){
51347         
51348         this.response = response;
51349         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51350         this.form.afterAction(this, false);
51351     },
51352
51353     processResponse : function(response){
51354         this.response = response;
51355         if(!response.responseText){
51356             return true;
51357         }
51358         this.result = this.handleResponse(response);
51359         return this.result;
51360     },
51361
51362     // utility functions used internally
51363     getUrl : function(appendParams){
51364         var url = this.options.url || this.form.url || this.form.el.dom.action;
51365         if(appendParams){
51366             var p = this.getParams();
51367             if(p){
51368                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51369             }
51370         }
51371         return url;
51372     },
51373
51374     getMethod : function(){
51375         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51376     },
51377
51378     getParams : function(){
51379         var bp = this.form.baseParams;
51380         var p = this.options.params;
51381         if(p){
51382             if(typeof p == "object"){
51383                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51384             }else if(typeof p == 'string' && bp){
51385                 p += '&' + Roo.urlEncode(bp);
51386             }
51387         }else if(bp){
51388             p = Roo.urlEncode(bp);
51389         }
51390         return p;
51391     },
51392
51393     createCallback : function(){
51394         return {
51395             success: this.success,
51396             failure: this.failure,
51397             scope: this,
51398             timeout: (this.form.timeout*1000),
51399             upload: this.form.fileUpload ? this.success : undefined
51400         };
51401     }
51402 };
51403
51404 Roo.form.Action.Submit = function(form, options){
51405     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51406 };
51407
51408 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51409     type : 'submit',
51410
51411     haveProgress : false,
51412     uploadComplete : false,
51413     
51414     // uploadProgress indicator.
51415     uploadProgress : function()
51416     {
51417         if (!this.form.progressUrl) {
51418             return;
51419         }
51420         
51421         if (!this.haveProgress) {
51422             Roo.MessageBox.progress("Uploading", "Uploading");
51423         }
51424         if (this.uploadComplete) {
51425            Roo.MessageBox.hide();
51426            return;
51427         }
51428         
51429         this.haveProgress = true;
51430    
51431         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51432         
51433         var c = new Roo.data.Connection();
51434         c.request({
51435             url : this.form.progressUrl,
51436             params: {
51437                 id : uid
51438             },
51439             method: 'GET',
51440             success : function(req){
51441                //console.log(data);
51442                 var rdata = false;
51443                 var edata;
51444                 try  {
51445                    rdata = Roo.decode(req.responseText)
51446                 } catch (e) {
51447                     Roo.log("Invalid data from server..");
51448                     Roo.log(edata);
51449                     return;
51450                 }
51451                 if (!rdata || !rdata.success) {
51452                     Roo.log(rdata);
51453                     Roo.MessageBox.alert(Roo.encode(rdata));
51454                     return;
51455                 }
51456                 var data = rdata.data;
51457                 
51458                 if (this.uploadComplete) {
51459                    Roo.MessageBox.hide();
51460                    return;
51461                 }
51462                    
51463                 if (data){
51464                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51465                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51466                     );
51467                 }
51468                 this.uploadProgress.defer(2000,this);
51469             },
51470        
51471             failure: function(data) {
51472                 Roo.log('progress url failed ');
51473                 Roo.log(data);
51474             },
51475             scope : this
51476         });
51477            
51478     },
51479     
51480     
51481     run : function()
51482     {
51483         // run get Values on the form, so it syncs any secondary forms.
51484         this.form.getValues();
51485         
51486         var o = this.options;
51487         var method = this.getMethod();
51488         var isPost = method == 'POST';
51489         if(o.clientValidation === false || this.form.isValid()){
51490             
51491             if (this.form.progressUrl) {
51492                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51493                     (new Date() * 1) + '' + Math.random());
51494                     
51495             } 
51496             
51497             
51498             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51499                 form:this.form.el.dom,
51500                 url:this.getUrl(!isPost),
51501                 method: method,
51502                 params:isPost ? this.getParams() : null,
51503                 isUpload: this.form.fileUpload,
51504                 formData : this.form.formData
51505             }));
51506             
51507             this.uploadProgress();
51508
51509         }else if (o.clientValidation !== false){ // client validation failed
51510             this.failureType = Roo.form.Action.CLIENT_INVALID;
51511             this.form.afterAction(this, false);
51512         }
51513     },
51514
51515     success : function(response)
51516     {
51517         this.uploadComplete= true;
51518         if (this.haveProgress) {
51519             Roo.MessageBox.hide();
51520         }
51521         
51522         
51523         var result = this.processResponse(response);
51524         if(result === true || result.success){
51525             this.form.afterAction(this, true);
51526             return;
51527         }
51528         if(result.errors){
51529             this.form.markInvalid(result.errors);
51530             this.failureType = Roo.form.Action.SERVER_INVALID;
51531         }
51532         this.form.afterAction(this, false);
51533     },
51534     failure : function(response)
51535     {
51536         this.uploadComplete= true;
51537         if (this.haveProgress) {
51538             Roo.MessageBox.hide();
51539         }
51540         
51541         this.response = response;
51542         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51543         this.form.afterAction(this, false);
51544     },
51545     
51546     handleResponse : function(response){
51547         if(this.form.errorReader){
51548             var rs = this.form.errorReader.read(response);
51549             var errors = [];
51550             if(rs.records){
51551                 for(var i = 0, len = rs.records.length; i < len; i++) {
51552                     var r = rs.records[i];
51553                     errors[i] = r.data;
51554                 }
51555             }
51556             if(errors.length < 1){
51557                 errors = null;
51558             }
51559             return {
51560                 success : rs.success,
51561                 errors : errors
51562             };
51563         }
51564         var ret = false;
51565         try {
51566             ret = Roo.decode(response.responseText);
51567         } catch (e) {
51568             ret = {
51569                 success: false,
51570                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51571                 errors : []
51572             };
51573         }
51574         return ret;
51575         
51576     }
51577 });
51578
51579
51580 Roo.form.Action.Load = function(form, options){
51581     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51582     this.reader = this.form.reader;
51583 };
51584
51585 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51586     type : 'load',
51587
51588     run : function(){
51589         
51590         Roo.Ajax.request(Roo.apply(
51591                 this.createCallback(), {
51592                     method:this.getMethod(),
51593                     url:this.getUrl(false),
51594                     params:this.getParams()
51595         }));
51596     },
51597
51598     success : function(response){
51599         
51600         var result = this.processResponse(response);
51601         if(result === true || !result.success || !result.data){
51602             this.failureType = Roo.form.Action.LOAD_FAILURE;
51603             this.form.afterAction(this, false);
51604             return;
51605         }
51606         this.form.clearInvalid();
51607         this.form.setValues(result.data);
51608         this.form.afterAction(this, true);
51609     },
51610
51611     handleResponse : function(response){
51612         if(this.form.reader){
51613             var rs = this.form.reader.read(response);
51614             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51615             return {
51616                 success : rs.success,
51617                 data : data
51618             };
51619         }
51620         return Roo.decode(response.responseText);
51621     }
51622 });
51623
51624 Roo.form.Action.ACTION_TYPES = {
51625     'load' : Roo.form.Action.Load,
51626     'submit' : Roo.form.Action.Submit
51627 };/*
51628  * Based on:
51629  * Ext JS Library 1.1.1
51630  * Copyright(c) 2006-2007, Ext JS, LLC.
51631  *
51632  * Originally Released Under LGPL - original licence link has changed is not relivant.
51633  *
51634  * Fork - LGPL
51635  * <script type="text/javascript">
51636  */
51637  
51638 /**
51639  * @class Roo.form.Layout
51640  * @extends Roo.Component
51641  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51642  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51643  * @constructor
51644  * @param {Object} config Configuration options
51645  */
51646 Roo.form.Layout = function(config){
51647     var xitems = [];
51648     if (config.items) {
51649         xitems = config.items;
51650         delete config.items;
51651     }
51652     Roo.form.Layout.superclass.constructor.call(this, config);
51653     this.stack = [];
51654     Roo.each(xitems, this.addxtype, this);
51655      
51656 };
51657
51658 Roo.extend(Roo.form.Layout, Roo.Component, {
51659     /**
51660      * @cfg {String/Object} autoCreate
51661      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51662      */
51663     /**
51664      * @cfg {String/Object/Function} style
51665      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51666      * a function which returns such a specification.
51667      */
51668     /**
51669      * @cfg {String} labelAlign
51670      * Valid values are "left," "top" and "right" (defaults to "left")
51671      */
51672     /**
51673      * @cfg {Number} labelWidth
51674      * Fixed width in pixels of all field labels (defaults to undefined)
51675      */
51676     /**
51677      * @cfg {Boolean} clear
51678      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51679      */
51680     clear : true,
51681     /**
51682      * @cfg {String} labelSeparator
51683      * The separator to use after field labels (defaults to ':')
51684      */
51685     labelSeparator : ':',
51686     /**
51687      * @cfg {Boolean} hideLabels
51688      * True to suppress the display of field labels in this layout (defaults to false)
51689      */
51690     hideLabels : false,
51691
51692     // private
51693     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51694     
51695     isLayout : true,
51696     
51697     // private
51698     onRender : function(ct, position){
51699         if(this.el){ // from markup
51700             this.el = Roo.get(this.el);
51701         }else {  // generate
51702             var cfg = this.getAutoCreate();
51703             this.el = ct.createChild(cfg, position);
51704         }
51705         if(this.style){
51706             this.el.applyStyles(this.style);
51707         }
51708         if(this.labelAlign){
51709             this.el.addClass('x-form-label-'+this.labelAlign);
51710         }
51711         if(this.hideLabels){
51712             this.labelStyle = "display:none";
51713             this.elementStyle = "padding-left:0;";
51714         }else{
51715             if(typeof this.labelWidth == 'number'){
51716                 this.labelStyle = "width:"+this.labelWidth+"px;";
51717                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51718             }
51719             if(this.labelAlign == 'top'){
51720                 this.labelStyle = "width:auto;";
51721                 this.elementStyle = "padding-left:0;";
51722             }
51723         }
51724         var stack = this.stack;
51725         var slen = stack.length;
51726         if(slen > 0){
51727             if(!this.fieldTpl){
51728                 var t = new Roo.Template(
51729                     '<div class="x-form-item {5}">',
51730                         '<label for="{0}" style="{2}">{1}{4}</label>',
51731                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51732                         '</div>',
51733                     '</div><div class="x-form-clear-left"></div>'
51734                 );
51735                 t.disableFormats = true;
51736                 t.compile();
51737                 Roo.form.Layout.prototype.fieldTpl = t;
51738             }
51739             for(var i = 0; i < slen; i++) {
51740                 if(stack[i].isFormField){
51741                     this.renderField(stack[i]);
51742                 }else{
51743                     this.renderComponent(stack[i]);
51744                 }
51745             }
51746         }
51747         if(this.clear){
51748             this.el.createChild({cls:'x-form-clear'});
51749         }
51750     },
51751
51752     // private
51753     renderField : function(f){
51754         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51755                f.id, //0
51756                f.fieldLabel, //1
51757                f.labelStyle||this.labelStyle||'', //2
51758                this.elementStyle||'', //3
51759                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51760                f.itemCls||this.itemCls||''  //5
51761        ], true).getPrevSibling());
51762     },
51763
51764     // private
51765     renderComponent : function(c){
51766         c.render(c.isLayout ? this.el : this.el.createChild());    
51767     },
51768     /**
51769      * Adds a object form elements (using the xtype property as the factory method.)
51770      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51771      * @param {Object} config 
51772      */
51773     addxtype : function(o)
51774     {
51775         // create the lement.
51776         o.form = this.form;
51777         var fe = Roo.factory(o, Roo.form);
51778         this.form.allItems.push(fe);
51779         this.stack.push(fe);
51780         
51781         if (fe.isFormField) {
51782             this.form.items.add(fe);
51783         }
51784          
51785         return fe;
51786     }
51787 });
51788
51789 /**
51790  * @class Roo.form.Column
51791  * @extends Roo.form.Layout
51792  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51793  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51794  * @constructor
51795  * @param {Object} config Configuration options
51796  */
51797 Roo.form.Column = function(config){
51798     Roo.form.Column.superclass.constructor.call(this, config);
51799 };
51800
51801 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51802     /**
51803      * @cfg {Number/String} width
51804      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51805      */
51806     /**
51807      * @cfg {String/Object} autoCreate
51808      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51809      */
51810
51811     // private
51812     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51813
51814     // private
51815     onRender : function(ct, position){
51816         Roo.form.Column.superclass.onRender.call(this, ct, position);
51817         if(this.width){
51818             this.el.setWidth(this.width);
51819         }
51820     }
51821 });
51822
51823
51824 /**
51825  * @class Roo.form.Row
51826  * @extends Roo.form.Layout
51827  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51828  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51829  * @constructor
51830  * @param {Object} config Configuration options
51831  */
51832
51833  
51834 Roo.form.Row = function(config){
51835     Roo.form.Row.superclass.constructor.call(this, config);
51836 };
51837  
51838 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51839       /**
51840      * @cfg {Number/String} width
51841      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51842      */
51843     /**
51844      * @cfg {Number/String} height
51845      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51846      */
51847     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51848     
51849     padWidth : 20,
51850     // private
51851     onRender : function(ct, position){
51852         //console.log('row render');
51853         if(!this.rowTpl){
51854             var t = new Roo.Template(
51855                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51856                     '<label for="{0}" style="{2}">{1}{4}</label>',
51857                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51858                     '</div>',
51859                 '</div>'
51860             );
51861             t.disableFormats = true;
51862             t.compile();
51863             Roo.form.Layout.prototype.rowTpl = t;
51864         }
51865         this.fieldTpl = this.rowTpl;
51866         
51867         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51868         var labelWidth = 100;
51869         
51870         if ((this.labelAlign != 'top')) {
51871             if (typeof this.labelWidth == 'number') {
51872                 labelWidth = this.labelWidth
51873             }
51874             this.padWidth =  20 + labelWidth;
51875             
51876         }
51877         
51878         Roo.form.Column.superclass.onRender.call(this, ct, position);
51879         if(this.width){
51880             this.el.setWidth(this.width);
51881         }
51882         if(this.height){
51883             this.el.setHeight(this.height);
51884         }
51885     },
51886     
51887     // private
51888     renderField : function(f){
51889         f.fieldEl = this.fieldTpl.append(this.el, [
51890                f.id, f.fieldLabel,
51891                f.labelStyle||this.labelStyle||'',
51892                this.elementStyle||'',
51893                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51894                f.itemCls||this.itemCls||'',
51895                f.width ? f.width + this.padWidth : 160 + this.padWidth
51896        ],true);
51897     }
51898 });
51899  
51900
51901 /**
51902  * @class Roo.form.FieldSet
51903  * @extends Roo.form.Layout
51904  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51905  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51906  * @constructor
51907  * @param {Object} config Configuration options
51908  */
51909 Roo.form.FieldSet = function(config){
51910     Roo.form.FieldSet.superclass.constructor.call(this, config);
51911 };
51912
51913 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51914     /**
51915      * @cfg {String} legend
51916      * The text to display as the legend for the FieldSet (defaults to '')
51917      */
51918     /**
51919      * @cfg {String/Object} autoCreate
51920      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51921      */
51922
51923     // private
51924     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51925
51926     // private
51927     onRender : function(ct, position){
51928         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51929         if(this.legend){
51930             this.setLegend(this.legend);
51931         }
51932     },
51933
51934     // private
51935     setLegend : function(text){
51936         if(this.rendered){
51937             this.el.child('legend').update(text);
51938         }
51939     }
51940 });/*
51941  * Based on:
51942  * Ext JS Library 1.1.1
51943  * Copyright(c) 2006-2007, Ext JS, LLC.
51944  *
51945  * Originally Released Under LGPL - original licence link has changed is not relivant.
51946  *
51947  * Fork - LGPL
51948  * <script type="text/javascript">
51949  */
51950 /**
51951  * @class Roo.form.VTypes
51952  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51953  * @static
51954  */
51955 Roo.form.VTypes = function(){
51956     // closure these in so they are only created once.
51957     var alpha = /^[a-zA-Z_]+$/;
51958     var alphanum = /^[a-zA-Z0-9_]+$/;
51959     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51960     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51961
51962     // All these messages and functions are configurable
51963     return {
51964         /**
51965          * The function used to validate email addresses
51966          * @param {String} value The email address
51967          */
51968         'email' : function(v){
51969             return email.test(v);
51970         },
51971         /**
51972          * The error text to display when the email validation function returns false
51973          * @type String
51974          */
51975         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51976         /**
51977          * The keystroke filter mask to be applied on email input
51978          * @type RegExp
51979          */
51980         'emailMask' : /[a-z0-9_\.\-@]/i,
51981
51982         /**
51983          * The function used to validate URLs
51984          * @param {String} value The URL
51985          */
51986         'url' : function(v){
51987             return url.test(v);
51988         },
51989         /**
51990          * The error text to display when the url validation function returns false
51991          * @type String
51992          */
51993         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51994         
51995         /**
51996          * The function used to validate alpha values
51997          * @param {String} value The value
51998          */
51999         'alpha' : function(v){
52000             return alpha.test(v);
52001         },
52002         /**
52003          * The error text to display when the alpha validation function returns false
52004          * @type String
52005          */
52006         'alphaText' : 'This field should only contain letters and _',
52007         /**
52008          * The keystroke filter mask to be applied on alpha input
52009          * @type RegExp
52010          */
52011         'alphaMask' : /[a-z_]/i,
52012
52013         /**
52014          * The function used to validate alphanumeric values
52015          * @param {String} value The value
52016          */
52017         'alphanum' : function(v){
52018             return alphanum.test(v);
52019         },
52020         /**
52021          * The error text to display when the alphanumeric validation function returns false
52022          * @type String
52023          */
52024         'alphanumText' : 'This field should only contain letters, numbers and _',
52025         /**
52026          * The keystroke filter mask to be applied on alphanumeric input
52027          * @type RegExp
52028          */
52029         'alphanumMask' : /[a-z0-9_]/i
52030     };
52031 }();//<script type="text/javascript">
52032
52033 /**
52034  * @class Roo.form.FCKeditor
52035  * @extends Roo.form.TextArea
52036  * Wrapper around the FCKEditor http://www.fckeditor.net
52037  * @constructor
52038  * Creates a new FCKeditor
52039  * @param {Object} config Configuration options
52040  */
52041 Roo.form.FCKeditor = function(config){
52042     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52043     this.addEvents({
52044          /**
52045          * @event editorinit
52046          * Fired when the editor is initialized - you can add extra handlers here..
52047          * @param {FCKeditor} this
52048          * @param {Object} the FCK object.
52049          */
52050         editorinit : true
52051     });
52052     
52053     
52054 };
52055 Roo.form.FCKeditor.editors = { };
52056 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52057 {
52058     //defaultAutoCreate : {
52059     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52060     //},
52061     // private
52062     /**
52063      * @cfg {Object} fck options - see fck manual for details.
52064      */
52065     fckconfig : false,
52066     
52067     /**
52068      * @cfg {Object} fck toolbar set (Basic or Default)
52069      */
52070     toolbarSet : 'Basic',
52071     /**
52072      * @cfg {Object} fck BasePath
52073      */ 
52074     basePath : '/fckeditor/',
52075     
52076     
52077     frame : false,
52078     
52079     value : '',
52080     
52081    
52082     onRender : function(ct, position)
52083     {
52084         if(!this.el){
52085             this.defaultAutoCreate = {
52086                 tag: "textarea",
52087                 style:"width:300px;height:60px;",
52088                 autocomplete: "new-password"
52089             };
52090         }
52091         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52092         /*
52093         if(this.grow){
52094             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52095             if(this.preventScrollbars){
52096                 this.el.setStyle("overflow", "hidden");
52097             }
52098             this.el.setHeight(this.growMin);
52099         }
52100         */
52101         //console.log('onrender' + this.getId() );
52102         Roo.form.FCKeditor.editors[this.getId()] = this;
52103          
52104
52105         this.replaceTextarea() ;
52106         
52107     },
52108     
52109     getEditor : function() {
52110         return this.fckEditor;
52111     },
52112     /**
52113      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52114      * @param {Mixed} value The value to set
52115      */
52116     
52117     
52118     setValue : function(value)
52119     {
52120         //console.log('setValue: ' + value);
52121         
52122         if(typeof(value) == 'undefined') { // not sure why this is happending...
52123             return;
52124         }
52125         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52126         
52127         //if(!this.el || !this.getEditor()) {
52128         //    this.value = value;
52129             //this.setValue.defer(100,this,[value]);    
52130         //    return;
52131         //} 
52132         
52133         if(!this.getEditor()) {
52134             return;
52135         }
52136         
52137         this.getEditor().SetData(value);
52138         
52139         //
52140
52141     },
52142
52143     /**
52144      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52145      * @return {Mixed} value The field value
52146      */
52147     getValue : function()
52148     {
52149         
52150         if (this.frame && this.frame.dom.style.display == 'none') {
52151             return Roo.form.FCKeditor.superclass.getValue.call(this);
52152         }
52153         
52154         if(!this.el || !this.getEditor()) {
52155            
52156            // this.getValue.defer(100,this); 
52157             return this.value;
52158         }
52159        
52160         
52161         var value=this.getEditor().GetData();
52162         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52163         return Roo.form.FCKeditor.superclass.getValue.call(this);
52164         
52165
52166     },
52167
52168     /**
52169      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52170      * @return {Mixed} value The field value
52171      */
52172     getRawValue : function()
52173     {
52174         if (this.frame && this.frame.dom.style.display == 'none') {
52175             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52176         }
52177         
52178         if(!this.el || !this.getEditor()) {
52179             //this.getRawValue.defer(100,this); 
52180             return this.value;
52181             return;
52182         }
52183         
52184         
52185         
52186         var value=this.getEditor().GetData();
52187         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52188         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52189          
52190     },
52191     
52192     setSize : function(w,h) {
52193         
52194         
52195         
52196         //if (this.frame && this.frame.dom.style.display == 'none') {
52197         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52198         //    return;
52199         //}
52200         //if(!this.el || !this.getEditor()) {
52201         //    this.setSize.defer(100,this, [w,h]); 
52202         //    return;
52203         //}
52204         
52205         
52206         
52207         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52208         
52209         this.frame.dom.setAttribute('width', w);
52210         this.frame.dom.setAttribute('height', h);
52211         this.frame.setSize(w,h);
52212         
52213     },
52214     
52215     toggleSourceEdit : function(value) {
52216         
52217       
52218          
52219         this.el.dom.style.display = value ? '' : 'none';
52220         this.frame.dom.style.display = value ?  'none' : '';
52221         
52222     },
52223     
52224     
52225     focus: function(tag)
52226     {
52227         if (this.frame.dom.style.display == 'none') {
52228             return Roo.form.FCKeditor.superclass.focus.call(this);
52229         }
52230         if(!this.el || !this.getEditor()) {
52231             this.focus.defer(100,this, [tag]); 
52232             return;
52233         }
52234         
52235         
52236         
52237         
52238         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52239         this.getEditor().Focus();
52240         if (tgs.length) {
52241             if (!this.getEditor().Selection.GetSelection()) {
52242                 this.focus.defer(100,this, [tag]); 
52243                 return;
52244             }
52245             
52246             
52247             var r = this.getEditor().EditorDocument.createRange();
52248             r.setStart(tgs[0],0);
52249             r.setEnd(tgs[0],0);
52250             this.getEditor().Selection.GetSelection().removeAllRanges();
52251             this.getEditor().Selection.GetSelection().addRange(r);
52252             this.getEditor().Focus();
52253         }
52254         
52255     },
52256     
52257     
52258     
52259     replaceTextarea : function()
52260     {
52261         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52262             return ;
52263         }
52264         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52265         //{
52266             // We must check the elements firstly using the Id and then the name.
52267         var oTextarea = document.getElementById( this.getId() );
52268         
52269         var colElementsByName = document.getElementsByName( this.getId() ) ;
52270          
52271         oTextarea.style.display = 'none' ;
52272
52273         if ( oTextarea.tabIndex ) {            
52274             this.TabIndex = oTextarea.tabIndex ;
52275         }
52276         
52277         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52278         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52279         this.frame = Roo.get(this.getId() + '___Frame')
52280     },
52281     
52282     _getConfigHtml : function()
52283     {
52284         var sConfig = '' ;
52285
52286         for ( var o in this.fckconfig ) {
52287             sConfig += sConfig.length > 0  ? '&amp;' : '';
52288             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52289         }
52290
52291         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52292     },
52293     
52294     
52295     _getIFrameHtml : function()
52296     {
52297         var sFile = 'fckeditor.html' ;
52298         /* no idea what this is about..
52299         try
52300         {
52301             if ( (/fcksource=true/i).test( window.top.location.search ) )
52302                 sFile = 'fckeditor.original.html' ;
52303         }
52304         catch (e) { 
52305         */
52306
52307         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52308         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52309         
52310         
52311         var html = '<iframe id="' + this.getId() +
52312             '___Frame" src="' + sLink +
52313             '" width="' + this.width +
52314             '" height="' + this.height + '"' +
52315             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52316             ' frameborder="0" scrolling="no"></iframe>' ;
52317
52318         return html ;
52319     },
52320     
52321     _insertHtmlBefore : function( html, element )
52322     {
52323         if ( element.insertAdjacentHTML )       {
52324             // IE
52325             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52326         } else { // Gecko
52327             var oRange = document.createRange() ;
52328             oRange.setStartBefore( element ) ;
52329             var oFragment = oRange.createContextualFragment( html );
52330             element.parentNode.insertBefore( oFragment, element ) ;
52331         }
52332     }
52333     
52334     
52335   
52336     
52337     
52338     
52339     
52340
52341 });
52342
52343 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52344
52345 function FCKeditor_OnComplete(editorInstance){
52346     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52347     f.fckEditor = editorInstance;
52348     //console.log("loaded");
52349     f.fireEvent('editorinit', f, editorInstance);
52350
52351   
52352
52353  
52354
52355
52356
52357
52358
52359
52360
52361
52362
52363
52364
52365
52366
52367
52368
52369 //<script type="text/javascript">
52370 /**
52371  * @class Roo.form.GridField
52372  * @extends Roo.form.Field
52373  * Embed a grid (or editable grid into a form)
52374  * STATUS ALPHA
52375  * 
52376  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52377  * it needs 
52378  * xgrid.store = Roo.data.Store
52379  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52380  * xgrid.store.reader = Roo.data.JsonReader 
52381  * 
52382  * 
52383  * @constructor
52384  * Creates a new GridField
52385  * @param {Object} config Configuration options
52386  */
52387 Roo.form.GridField = function(config){
52388     Roo.form.GridField.superclass.constructor.call(this, config);
52389      
52390 };
52391
52392 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52393     /**
52394      * @cfg {Number} width  - used to restrict width of grid..
52395      */
52396     width : 100,
52397     /**
52398      * @cfg {Number} height - used to restrict height of grid..
52399      */
52400     height : 50,
52401      /**
52402      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52403          * 
52404          *}
52405      */
52406     xgrid : false, 
52407     /**
52408      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52409      * {tag: "input", type: "checkbox", autocomplete: "off"})
52410      */
52411    // defaultAutoCreate : { tag: 'div' },
52412     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52413     /**
52414      * @cfg {String} addTitle Text to include for adding a title.
52415      */
52416     addTitle : false,
52417     //
52418     onResize : function(){
52419         Roo.form.Field.superclass.onResize.apply(this, arguments);
52420     },
52421
52422     initEvents : function(){
52423         // Roo.form.Checkbox.superclass.initEvents.call(this);
52424         // has no events...
52425        
52426     },
52427
52428
52429     getResizeEl : function(){
52430         return this.wrap;
52431     },
52432
52433     getPositionEl : function(){
52434         return this.wrap;
52435     },
52436
52437     // private
52438     onRender : function(ct, position){
52439         
52440         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52441         var style = this.style;
52442         delete this.style;
52443         
52444         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52445         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52446         this.viewEl = this.wrap.createChild({ tag: 'div' });
52447         if (style) {
52448             this.viewEl.applyStyles(style);
52449         }
52450         if (this.width) {
52451             this.viewEl.setWidth(this.width);
52452         }
52453         if (this.height) {
52454             this.viewEl.setHeight(this.height);
52455         }
52456         //if(this.inputValue !== undefined){
52457         //this.setValue(this.value);
52458         
52459         
52460         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52461         
52462         
52463         this.grid.render();
52464         this.grid.getDataSource().on('remove', this.refreshValue, this);
52465         this.grid.getDataSource().on('update', this.refreshValue, this);
52466         this.grid.on('afteredit', this.refreshValue, this);
52467  
52468     },
52469      
52470     
52471     /**
52472      * Sets the value of the item. 
52473      * @param {String} either an object  or a string..
52474      */
52475     setValue : function(v){
52476         //this.value = v;
52477         v = v || []; // empty set..
52478         // this does not seem smart - it really only affects memoryproxy grids..
52479         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52480             var ds = this.grid.getDataSource();
52481             // assumes a json reader..
52482             var data = {}
52483             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52484             ds.loadData( data);
52485         }
52486         // clear selection so it does not get stale.
52487         if (this.grid.sm) { 
52488             this.grid.sm.clearSelections();
52489         }
52490         
52491         Roo.form.GridField.superclass.setValue.call(this, v);
52492         this.refreshValue();
52493         // should load data in the grid really....
52494     },
52495     
52496     // private
52497     refreshValue: function() {
52498          var val = [];
52499         this.grid.getDataSource().each(function(r) {
52500             val.push(r.data);
52501         });
52502         this.el.dom.value = Roo.encode(val);
52503     }
52504     
52505      
52506     
52507     
52508 });/*
52509  * Based on:
52510  * Ext JS Library 1.1.1
52511  * Copyright(c) 2006-2007, Ext JS, LLC.
52512  *
52513  * Originally Released Under LGPL - original licence link has changed is not relivant.
52514  *
52515  * Fork - LGPL
52516  * <script type="text/javascript">
52517  */
52518 /**
52519  * @class Roo.form.DisplayField
52520  * @extends Roo.form.Field
52521  * A generic Field to display non-editable data.
52522  * @cfg {Boolean} closable (true|false) default false
52523  * @constructor
52524  * Creates a new Display Field item.
52525  * @param {Object} config Configuration options
52526  */
52527 Roo.form.DisplayField = function(config){
52528     Roo.form.DisplayField.superclass.constructor.call(this, config);
52529     
52530     this.addEvents({
52531         /**
52532          * @event close
52533          * Fires after the click the close btn
52534              * @param {Roo.form.DisplayField} this
52535              */
52536         close : true
52537     });
52538 };
52539
52540 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52541     inputType:      'hidden',
52542     allowBlank:     true,
52543     readOnly:         true,
52544     
52545  
52546     /**
52547      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52548      */
52549     focusClass : undefined,
52550     /**
52551      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52552      */
52553     fieldClass: 'x-form-field',
52554     
52555      /**
52556      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52557      */
52558     valueRenderer: undefined,
52559     
52560     width: 100,
52561     /**
52562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52563      * {tag: "input", type: "checkbox", autocomplete: "off"})
52564      */
52565      
52566  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52567  
52568     closable : false,
52569     
52570     onResize : function(){
52571         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52572         
52573     },
52574
52575     initEvents : function(){
52576         // Roo.form.Checkbox.superclass.initEvents.call(this);
52577         // has no events...
52578         
52579         if(this.closable){
52580             this.closeEl.on('click', this.onClose, this);
52581         }
52582        
52583     },
52584
52585
52586     getResizeEl : function(){
52587         return this.wrap;
52588     },
52589
52590     getPositionEl : function(){
52591         return this.wrap;
52592     },
52593
52594     // private
52595     onRender : function(ct, position){
52596         
52597         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52598         //if(this.inputValue !== undefined){
52599         this.wrap = this.el.wrap();
52600         
52601         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52602         
52603         if(this.closable){
52604             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52605         }
52606         
52607         if (this.bodyStyle) {
52608             this.viewEl.applyStyles(this.bodyStyle);
52609         }
52610         //this.viewEl.setStyle('padding', '2px');
52611         
52612         this.setValue(this.value);
52613         
52614     },
52615 /*
52616     // private
52617     initValue : Roo.emptyFn,
52618
52619   */
52620
52621         // private
52622     onClick : function(){
52623         
52624     },
52625
52626     /**
52627      * Sets the checked state of the checkbox.
52628      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52629      */
52630     setValue : function(v){
52631         this.value = v;
52632         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52633         // this might be called before we have a dom element..
52634         if (!this.viewEl) {
52635             return;
52636         }
52637         this.viewEl.dom.innerHTML = html;
52638         Roo.form.DisplayField.superclass.setValue.call(this, v);
52639
52640     },
52641     
52642     onClose : function(e)
52643     {
52644         e.preventDefault();
52645         
52646         this.fireEvent('close', this);
52647     }
52648 });/*
52649  * 
52650  * Licence- LGPL
52651  * 
52652  */
52653
52654 /**
52655  * @class Roo.form.DayPicker
52656  * @extends Roo.form.Field
52657  * A Day picker show [M] [T] [W] ....
52658  * @constructor
52659  * Creates a new Day Picker
52660  * @param {Object} config Configuration options
52661  */
52662 Roo.form.DayPicker= function(config){
52663     Roo.form.DayPicker.superclass.constructor.call(this, config);
52664      
52665 };
52666
52667 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52668     /**
52669      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52670      */
52671     focusClass : undefined,
52672     /**
52673      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52674      */
52675     fieldClass: "x-form-field",
52676    
52677     /**
52678      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52679      * {tag: "input", type: "checkbox", autocomplete: "off"})
52680      */
52681     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52682     
52683    
52684     actionMode : 'viewEl', 
52685     //
52686     // private
52687  
52688     inputType : 'hidden',
52689     
52690      
52691     inputElement: false, // real input element?
52692     basedOn: false, // ????
52693     
52694     isFormField: true, // not sure where this is needed!!!!
52695
52696     onResize : function(){
52697         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52698         if(!this.boxLabel){
52699             this.el.alignTo(this.wrap, 'c-c');
52700         }
52701     },
52702
52703     initEvents : function(){
52704         Roo.form.Checkbox.superclass.initEvents.call(this);
52705         this.el.on("click", this.onClick,  this);
52706         this.el.on("change", this.onClick,  this);
52707     },
52708
52709
52710     getResizeEl : function(){
52711         return this.wrap;
52712     },
52713
52714     getPositionEl : function(){
52715         return this.wrap;
52716     },
52717
52718     
52719     // private
52720     onRender : function(ct, position){
52721         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52722        
52723         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52724         
52725         var r1 = '<table><tr>';
52726         var r2 = '<tr class="x-form-daypick-icons">';
52727         for (var i=0; i < 7; i++) {
52728             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52729             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52730         }
52731         
52732         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52733         viewEl.select('img').on('click', this.onClick, this);
52734         this.viewEl = viewEl;   
52735         
52736         
52737         // this will not work on Chrome!!!
52738         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52739         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52740         
52741         
52742           
52743
52744     },
52745
52746     // private
52747     initValue : Roo.emptyFn,
52748
52749     /**
52750      * Returns the checked state of the checkbox.
52751      * @return {Boolean} True if checked, else false
52752      */
52753     getValue : function(){
52754         return this.el.dom.value;
52755         
52756     },
52757
52758         // private
52759     onClick : function(e){ 
52760         //this.setChecked(!this.checked);
52761         Roo.get(e.target).toggleClass('x-menu-item-checked');
52762         this.refreshValue();
52763         //if(this.el.dom.checked != this.checked){
52764         //    this.setValue(this.el.dom.checked);
52765        // }
52766     },
52767     
52768     // private
52769     refreshValue : function()
52770     {
52771         var val = '';
52772         this.viewEl.select('img',true).each(function(e,i,n)  {
52773             val += e.is(".x-menu-item-checked") ? String(n) : '';
52774         });
52775         this.setValue(val, true);
52776     },
52777
52778     /**
52779      * Sets the checked state of the checkbox.
52780      * On is always based on a string comparison between inputValue and the param.
52781      * @param {Boolean/String} value - the value to set 
52782      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52783      */
52784     setValue : function(v,suppressEvent){
52785         if (!this.el.dom) {
52786             return;
52787         }
52788         var old = this.el.dom.value ;
52789         this.el.dom.value = v;
52790         if (suppressEvent) {
52791             return ;
52792         }
52793          
52794         // update display..
52795         this.viewEl.select('img',true).each(function(e,i,n)  {
52796             
52797             var on = e.is(".x-menu-item-checked");
52798             var newv = v.indexOf(String(n)) > -1;
52799             if (on != newv) {
52800                 e.toggleClass('x-menu-item-checked');
52801             }
52802             
52803         });
52804         
52805         
52806         this.fireEvent('change', this, v, old);
52807         
52808         
52809     },
52810    
52811     // handle setting of hidden value by some other method!!?!?
52812     setFromHidden: function()
52813     {
52814         if(!this.el){
52815             return;
52816         }
52817         //console.log("SET FROM HIDDEN");
52818         //alert('setFrom hidden');
52819         this.setValue(this.el.dom.value);
52820     },
52821     
52822     onDestroy : function()
52823     {
52824         if(this.viewEl){
52825             Roo.get(this.viewEl).remove();
52826         }
52827          
52828         Roo.form.DayPicker.superclass.onDestroy.call(this);
52829     }
52830
52831 });/*
52832  * RooJS Library 1.1.1
52833  * Copyright(c) 2008-2011  Alan Knowles
52834  *
52835  * License - LGPL
52836  */
52837  
52838
52839 /**
52840  * @class Roo.form.ComboCheck
52841  * @extends Roo.form.ComboBox
52842  * A combobox for multiple select items.
52843  *
52844  * FIXME - could do with a reset button..
52845  * 
52846  * @constructor
52847  * Create a new ComboCheck
52848  * @param {Object} config Configuration options
52849  */
52850 Roo.form.ComboCheck = function(config){
52851     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52852     // should verify some data...
52853     // like
52854     // hiddenName = required..
52855     // displayField = required
52856     // valudField == required
52857     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52858     var _t = this;
52859     Roo.each(req, function(e) {
52860         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52861             throw "Roo.form.ComboCheck : missing value for: " + e;
52862         }
52863     });
52864     
52865     
52866 };
52867
52868 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52869      
52870      
52871     editable : false,
52872      
52873     selectedClass: 'x-menu-item-checked', 
52874     
52875     // private
52876     onRender : function(ct, position){
52877         var _t = this;
52878         
52879         
52880         
52881         if(!this.tpl){
52882             var cls = 'x-combo-list';
52883
52884             
52885             this.tpl =  new Roo.Template({
52886                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52887                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52888                    '<span>{' + this.displayField + '}</span>' +
52889                     '</div>' 
52890                 
52891             });
52892         }
52893  
52894         
52895         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52896         this.view.singleSelect = false;
52897         this.view.multiSelect = true;
52898         this.view.toggleSelect = true;
52899         this.pageTb.add(new Roo.Toolbar.Fill(), {
52900             
52901             text: 'Done',
52902             handler: function()
52903             {
52904                 _t.collapse();
52905             }
52906         });
52907     },
52908     
52909     onViewOver : function(e, t){
52910         // do nothing...
52911         return;
52912         
52913     },
52914     
52915     onViewClick : function(doFocus,index){
52916         return;
52917         
52918     },
52919     select: function () {
52920         //Roo.log("SELECT CALLED");
52921     },
52922      
52923     selectByValue : function(xv, scrollIntoView){
52924         var ar = this.getValueArray();
52925         var sels = [];
52926         
52927         Roo.each(ar, function(v) {
52928             if(v === undefined || v === null){
52929                 return;
52930             }
52931             var r = this.findRecord(this.valueField, v);
52932             if(r){
52933                 sels.push(this.store.indexOf(r))
52934                 
52935             }
52936         },this);
52937         this.view.select(sels);
52938         return false;
52939     },
52940     
52941     
52942     
52943     onSelect : function(record, index){
52944        // Roo.log("onselect Called");
52945        // this is only called by the clear button now..
52946         this.view.clearSelections();
52947         this.setValue('[]');
52948         if (this.value != this.valueBefore) {
52949             this.fireEvent('change', this, this.value, this.valueBefore);
52950             this.valueBefore = this.value;
52951         }
52952     },
52953     getValueArray : function()
52954     {
52955         var ar = [] ;
52956         
52957         try {
52958             //Roo.log(this.value);
52959             if (typeof(this.value) == 'undefined') {
52960                 return [];
52961             }
52962             var ar = Roo.decode(this.value);
52963             return  ar instanceof Array ? ar : []; //?? valid?
52964             
52965         } catch(e) {
52966             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52967             return [];
52968         }
52969          
52970     },
52971     expand : function ()
52972     {
52973         
52974         Roo.form.ComboCheck.superclass.expand.call(this);
52975         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52976         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52977         
52978
52979     },
52980     
52981     collapse : function(){
52982         Roo.form.ComboCheck.superclass.collapse.call(this);
52983         var sl = this.view.getSelectedIndexes();
52984         var st = this.store;
52985         var nv = [];
52986         var tv = [];
52987         var r;
52988         Roo.each(sl, function(i) {
52989             r = st.getAt(i);
52990             nv.push(r.get(this.valueField));
52991         },this);
52992         this.setValue(Roo.encode(nv));
52993         if (this.value != this.valueBefore) {
52994
52995             this.fireEvent('change', this, this.value, this.valueBefore);
52996             this.valueBefore = this.value;
52997         }
52998         
52999     },
53000     
53001     setValue : function(v){
53002         // Roo.log(v);
53003         this.value = v;
53004         
53005         var vals = this.getValueArray();
53006         var tv = [];
53007         Roo.each(vals, function(k) {
53008             var r = this.findRecord(this.valueField, k);
53009             if(r){
53010                 tv.push(r.data[this.displayField]);
53011             }else if(this.valueNotFoundText !== undefined){
53012                 tv.push( this.valueNotFoundText );
53013             }
53014         },this);
53015        // Roo.log(tv);
53016         
53017         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53018         this.hiddenField.value = v;
53019         this.value = v;
53020     }
53021     
53022 });/*
53023  * Based on:
53024  * Ext JS Library 1.1.1
53025  * Copyright(c) 2006-2007, Ext JS, LLC.
53026  *
53027  * Originally Released Under LGPL - original licence link has changed is not relivant.
53028  *
53029  * Fork - LGPL
53030  * <script type="text/javascript">
53031  */
53032  
53033 /**
53034  * @class Roo.form.Signature
53035  * @extends Roo.form.Field
53036  * Signature field.  
53037  * @constructor
53038  * 
53039  * @param {Object} config Configuration options
53040  */
53041
53042 Roo.form.Signature = function(config){
53043     Roo.form.Signature.superclass.constructor.call(this, config);
53044     
53045     this.addEvents({// not in used??
53046          /**
53047          * @event confirm
53048          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53049              * @param {Roo.form.Signature} combo This combo box
53050              */
53051         'confirm' : true,
53052         /**
53053          * @event reset
53054          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53055              * @param {Roo.form.ComboBox} combo This combo box
53056              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53057              */
53058         'reset' : true
53059     });
53060 };
53061
53062 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53063     /**
53064      * @cfg {Object} labels Label to use when rendering a form.
53065      * defaults to 
53066      * labels : { 
53067      *      clear : "Clear",
53068      *      confirm : "Confirm"
53069      *  }
53070      */
53071     labels : { 
53072         clear : "Clear",
53073         confirm : "Confirm"
53074     },
53075     /**
53076      * @cfg {Number} width The signature panel width (defaults to 300)
53077      */
53078     width: 300,
53079     /**
53080      * @cfg {Number} height The signature panel height (defaults to 100)
53081      */
53082     height : 100,
53083     /**
53084      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53085      */
53086     allowBlank : false,
53087     
53088     //private
53089     // {Object} signPanel The signature SVG panel element (defaults to {})
53090     signPanel : {},
53091     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53092     isMouseDown : false,
53093     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53094     isConfirmed : false,
53095     // {String} signatureTmp SVG mapping string (defaults to empty string)
53096     signatureTmp : '',
53097     
53098     
53099     defaultAutoCreate : { // modified by initCompnoent..
53100         tag: "input",
53101         type:"hidden"
53102     },
53103
53104     // private
53105     onRender : function(ct, position){
53106         
53107         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53108         
53109         this.wrap = this.el.wrap({
53110             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53111         });
53112         
53113         this.createToolbar(this);
53114         this.signPanel = this.wrap.createChild({
53115                 tag: 'div',
53116                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53117             }, this.el
53118         );
53119             
53120         this.svgID = Roo.id();
53121         this.svgEl = this.signPanel.createChild({
53122               xmlns : 'http://www.w3.org/2000/svg',
53123               tag : 'svg',
53124               id : this.svgID + "-svg",
53125               width: this.width,
53126               height: this.height,
53127               viewBox: '0 0 '+this.width+' '+this.height,
53128               cn : [
53129                 {
53130                     tag: "rect",
53131                     id: this.svgID + "-svg-r",
53132                     width: this.width,
53133                     height: this.height,
53134                     fill: "#ffa"
53135                 },
53136                 {
53137                     tag: "line",
53138                     id: this.svgID + "-svg-l",
53139                     x1: "0", // start
53140                     y1: (this.height*0.8), // start set the line in 80% of height
53141                     x2: this.width, // end
53142                     y2: (this.height*0.8), // end set the line in 80% of height
53143                     'stroke': "#666",
53144                     'stroke-width': "1",
53145                     'stroke-dasharray': "3",
53146                     'shape-rendering': "crispEdges",
53147                     'pointer-events': "none"
53148                 },
53149                 {
53150                     tag: "path",
53151                     id: this.svgID + "-svg-p",
53152                     'stroke': "navy",
53153                     'stroke-width': "3",
53154                     'fill': "none",
53155                     'pointer-events': 'none'
53156                 }
53157               ]
53158         });
53159         this.createSVG();
53160         this.svgBox = this.svgEl.dom.getScreenCTM();
53161     },
53162     createSVG : function(){ 
53163         var svg = this.signPanel;
53164         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53165         var t = this;
53166
53167         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53168         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53169         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53170         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53171         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53172         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53173         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53174         
53175     },
53176     isTouchEvent : function(e){
53177         return e.type.match(/^touch/);
53178     },
53179     getCoords : function (e) {
53180         var pt    = this.svgEl.dom.createSVGPoint();
53181         pt.x = e.clientX; 
53182         pt.y = e.clientY;
53183         if (this.isTouchEvent(e)) {
53184             pt.x =  e.targetTouches[0].clientX;
53185             pt.y = e.targetTouches[0].clientY;
53186         }
53187         var a = this.svgEl.dom.getScreenCTM();
53188         var b = a.inverse();
53189         var mx = pt.matrixTransform(b);
53190         return mx.x + ',' + mx.y;
53191     },
53192     //mouse event headler 
53193     down : function (e) {
53194         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53195         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53196         
53197         this.isMouseDown = true;
53198         
53199         e.preventDefault();
53200     },
53201     move : function (e) {
53202         if (this.isMouseDown) {
53203             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53204             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53205         }
53206         
53207         e.preventDefault();
53208     },
53209     up : function (e) {
53210         this.isMouseDown = false;
53211         var sp = this.signatureTmp.split(' ');
53212         
53213         if(sp.length > 1){
53214             if(!sp[sp.length-2].match(/^L/)){
53215                 sp.pop();
53216                 sp.pop();
53217                 sp.push("");
53218                 this.signatureTmp = sp.join(" ");
53219             }
53220         }
53221         if(this.getValue() != this.signatureTmp){
53222             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53223             this.isConfirmed = false;
53224         }
53225         e.preventDefault();
53226     },
53227     
53228     /**
53229      * Protected method that will not generally be called directly. It
53230      * is called when the editor creates its toolbar. Override this method if you need to
53231      * add custom toolbar buttons.
53232      * @param {HtmlEditor} editor
53233      */
53234     createToolbar : function(editor){
53235          function btn(id, toggle, handler){
53236             var xid = fid + '-'+ id ;
53237             return {
53238                 id : xid,
53239                 cmd : id,
53240                 cls : 'x-btn-icon x-edit-'+id,
53241                 enableToggle:toggle !== false,
53242                 scope: editor, // was editor...
53243                 handler:handler||editor.relayBtnCmd,
53244                 clickEvent:'mousedown',
53245                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53246                 tabIndex:-1
53247             };
53248         }
53249         
53250         
53251         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53252         this.tb = tb;
53253         this.tb.add(
53254            {
53255                 cls : ' x-signature-btn x-signature-'+id,
53256                 scope: editor, // was editor...
53257                 handler: this.reset,
53258                 clickEvent:'mousedown',
53259                 text: this.labels.clear
53260             },
53261             {
53262                  xtype : 'Fill',
53263                  xns: Roo.Toolbar
53264             }, 
53265             {
53266                 cls : '  x-signature-btn x-signature-'+id,
53267                 scope: editor, // was editor...
53268                 handler: this.confirmHandler,
53269                 clickEvent:'mousedown',
53270                 text: this.labels.confirm
53271             }
53272         );
53273     
53274     },
53275     //public
53276     /**
53277      * when user is clicked confirm then show this image.....
53278      * 
53279      * @return {String} Image Data URI
53280      */
53281     getImageDataURI : function(){
53282         var svg = this.svgEl.dom.parentNode.innerHTML;
53283         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53284         return src; 
53285     },
53286     /**
53287      * 
53288      * @return {Boolean} this.isConfirmed
53289      */
53290     getConfirmed : function(){
53291         return this.isConfirmed;
53292     },
53293     /**
53294      * 
53295      * @return {Number} this.width
53296      */
53297     getWidth : function(){
53298         return this.width;
53299     },
53300     /**
53301      * 
53302      * @return {Number} this.height
53303      */
53304     getHeight : function(){
53305         return this.height;
53306     },
53307     // private
53308     getSignature : function(){
53309         return this.signatureTmp;
53310     },
53311     // private
53312     reset : function(){
53313         this.signatureTmp = '';
53314         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53315         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53316         this.isConfirmed = false;
53317         Roo.form.Signature.superclass.reset.call(this);
53318     },
53319     setSignature : function(s){
53320         this.signatureTmp = s;
53321         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53322         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53323         this.setValue(s);
53324         this.isConfirmed = false;
53325         Roo.form.Signature.superclass.reset.call(this);
53326     }, 
53327     test : function(){
53328 //        Roo.log(this.signPanel.dom.contentWindow.up())
53329     },
53330     //private
53331     setConfirmed : function(){
53332         
53333         
53334         
53335 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53336     },
53337     // private
53338     confirmHandler : function(){
53339         if(!this.getSignature()){
53340             return;
53341         }
53342         
53343         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53344         this.setValue(this.getSignature());
53345         this.isConfirmed = true;
53346         
53347         this.fireEvent('confirm', this);
53348     },
53349     // private
53350     // Subclasses should provide the validation implementation by overriding this
53351     validateValue : function(value){
53352         if(this.allowBlank){
53353             return true;
53354         }
53355         
53356         if(this.isConfirmed){
53357             return true;
53358         }
53359         return false;
53360     }
53361 });/*
53362  * Based on:
53363  * Ext JS Library 1.1.1
53364  * Copyright(c) 2006-2007, Ext JS, LLC.
53365  *
53366  * Originally Released Under LGPL - original licence link has changed is not relivant.
53367  *
53368  * Fork - LGPL
53369  * <script type="text/javascript">
53370  */
53371  
53372
53373 /**
53374  * @class Roo.form.ComboBox
53375  * @extends Roo.form.TriggerField
53376  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53377  * @constructor
53378  * Create a new ComboBox.
53379  * @param {Object} config Configuration options
53380  */
53381 Roo.form.Select = function(config){
53382     Roo.form.Select.superclass.constructor.call(this, config);
53383      
53384 };
53385
53386 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53387     /**
53388      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53389      */
53390     /**
53391      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53392      * rendering into an Roo.Editor, defaults to false)
53393      */
53394     /**
53395      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53396      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53397      */
53398     /**
53399      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53400      */
53401     /**
53402      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53403      * the dropdown list (defaults to undefined, with no header element)
53404      */
53405
53406      /**
53407      * @cfg {String/Roo.Template} tpl The template to use to render the output
53408      */
53409      
53410     // private
53411     defaultAutoCreate : {tag: "select"  },
53412     /**
53413      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53414      */
53415     listWidth: undefined,
53416     /**
53417      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53418      * mode = 'remote' or 'text' if mode = 'local')
53419      */
53420     displayField: undefined,
53421     /**
53422      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53423      * mode = 'remote' or 'value' if mode = 'local'). 
53424      * Note: use of a valueField requires the user make a selection
53425      * in order for a value to be mapped.
53426      */
53427     valueField: undefined,
53428     
53429     
53430     /**
53431      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53432      * field's data value (defaults to the underlying DOM element's name)
53433      */
53434     hiddenName: undefined,
53435     /**
53436      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53437      */
53438     listClass: '',
53439     /**
53440      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53441      */
53442     selectedClass: 'x-combo-selected',
53443     /**
53444      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53445      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53446      * which displays a downward arrow icon).
53447      */
53448     triggerClass : 'x-form-arrow-trigger',
53449     /**
53450      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53451      */
53452     shadow:'sides',
53453     /**
53454      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53455      * anchor positions (defaults to 'tl-bl')
53456      */
53457     listAlign: 'tl-bl?',
53458     /**
53459      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53460      */
53461     maxHeight: 300,
53462     /**
53463      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53464      * query specified by the allQuery config option (defaults to 'query')
53465      */
53466     triggerAction: 'query',
53467     /**
53468      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53469      * (defaults to 4, does not apply if editable = false)
53470      */
53471     minChars : 4,
53472     /**
53473      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53474      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53475      */
53476     typeAhead: false,
53477     /**
53478      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53479      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53480      */
53481     queryDelay: 500,
53482     /**
53483      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53484      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53485      */
53486     pageSize: 0,
53487     /**
53488      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53489      * when editable = true (defaults to false)
53490      */
53491     selectOnFocus:false,
53492     /**
53493      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53494      */
53495     queryParam: 'query',
53496     /**
53497      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53498      * when mode = 'remote' (defaults to 'Loading...')
53499      */
53500     loadingText: 'Loading...',
53501     /**
53502      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53503      */
53504     resizable: false,
53505     /**
53506      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53507      */
53508     handleHeight : 8,
53509     /**
53510      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53511      * traditional select (defaults to true)
53512      */
53513     editable: true,
53514     /**
53515      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53516      */
53517     allQuery: '',
53518     /**
53519      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53520      */
53521     mode: 'remote',
53522     /**
53523      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53524      * listWidth has a higher value)
53525      */
53526     minListWidth : 70,
53527     /**
53528      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53529      * allow the user to set arbitrary text into the field (defaults to false)
53530      */
53531     forceSelection:false,
53532     /**
53533      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53534      * if typeAhead = true (defaults to 250)
53535      */
53536     typeAheadDelay : 250,
53537     /**
53538      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53539      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53540      */
53541     valueNotFoundText : undefined,
53542     
53543     /**
53544      * @cfg {String} defaultValue The value displayed after loading the store.
53545      */
53546     defaultValue: '',
53547     
53548     /**
53549      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53550      */
53551     blockFocus : false,
53552     
53553     /**
53554      * @cfg {Boolean} disableClear Disable showing of clear button.
53555      */
53556     disableClear : false,
53557     /**
53558      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53559      */
53560     alwaysQuery : false,
53561     
53562     //private
53563     addicon : false,
53564     editicon: false,
53565     
53566     // element that contains real text value.. (when hidden is used..)
53567      
53568     // private
53569     onRender : function(ct, position){
53570         Roo.form.Field.prototype.onRender.call(this, ct, position);
53571         
53572         if(this.store){
53573             this.store.on('beforeload', this.onBeforeLoad, this);
53574             this.store.on('load', this.onLoad, this);
53575             this.store.on('loadexception', this.onLoadException, this);
53576             this.store.load({});
53577         }
53578         
53579         
53580         
53581     },
53582
53583     // private
53584     initEvents : function(){
53585         //Roo.form.ComboBox.superclass.initEvents.call(this);
53586  
53587     },
53588
53589     onDestroy : function(){
53590        
53591         if(this.store){
53592             this.store.un('beforeload', this.onBeforeLoad, this);
53593             this.store.un('load', this.onLoad, this);
53594             this.store.un('loadexception', this.onLoadException, this);
53595         }
53596         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53597     },
53598
53599     // private
53600     fireKey : function(e){
53601         if(e.isNavKeyPress() && !this.list.isVisible()){
53602             this.fireEvent("specialkey", this, e);
53603         }
53604     },
53605
53606     // private
53607     onResize: function(w, h){
53608         
53609         return; 
53610     
53611         
53612     },
53613
53614     /**
53615      * Allow or prevent the user from directly editing the field text.  If false is passed,
53616      * the user will only be able to select from the items defined in the dropdown list.  This method
53617      * is the runtime equivalent of setting the 'editable' config option at config time.
53618      * @param {Boolean} value True to allow the user to directly edit the field text
53619      */
53620     setEditable : function(value){
53621          
53622     },
53623
53624     // private
53625     onBeforeLoad : function(){
53626         
53627         Roo.log("Select before load");
53628         return;
53629     
53630         this.innerList.update(this.loadingText ?
53631                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53632         //this.restrictHeight();
53633         this.selectedIndex = -1;
53634     },
53635
53636     // private
53637     onLoad : function(){
53638
53639     
53640         var dom = this.el.dom;
53641         dom.innerHTML = '';
53642          var od = dom.ownerDocument;
53643          
53644         if (this.emptyText) {
53645             var op = od.createElement('option');
53646             op.setAttribute('value', '');
53647             op.innerHTML = String.format('{0}', this.emptyText);
53648             dom.appendChild(op);
53649         }
53650         if(this.store.getCount() > 0){
53651            
53652             var vf = this.valueField;
53653             var df = this.displayField;
53654             this.store.data.each(function(r) {
53655                 // which colmsn to use... testing - cdoe / title..
53656                 var op = od.createElement('option');
53657                 op.setAttribute('value', r.data[vf]);
53658                 op.innerHTML = String.format('{0}', r.data[df]);
53659                 dom.appendChild(op);
53660             });
53661             if (typeof(this.defaultValue != 'undefined')) {
53662                 this.setValue(this.defaultValue);
53663             }
53664             
53665              
53666         }else{
53667             //this.onEmptyResults();
53668         }
53669         //this.el.focus();
53670     },
53671     // private
53672     onLoadException : function()
53673     {
53674         dom.innerHTML = '';
53675             
53676         Roo.log("Select on load exception");
53677         return;
53678     
53679         this.collapse();
53680         Roo.log(this.store.reader.jsonData);
53681         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53682             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53683         }
53684         
53685         
53686     },
53687     // private
53688     onTypeAhead : function(){
53689          
53690     },
53691
53692     // private
53693     onSelect : function(record, index){
53694         Roo.log('on select?');
53695         return;
53696         if(this.fireEvent('beforeselect', this, record, index) !== false){
53697             this.setFromData(index > -1 ? record.data : false);
53698             this.collapse();
53699             this.fireEvent('select', this, record, index);
53700         }
53701     },
53702
53703     /**
53704      * Returns the currently selected field value or empty string if no value is set.
53705      * @return {String} value The selected value
53706      */
53707     getValue : function(){
53708         var dom = this.el.dom;
53709         this.value = dom.options[dom.selectedIndex].value;
53710         return this.value;
53711         
53712     },
53713
53714     /**
53715      * Clears any text/value currently set in the field
53716      */
53717     clearValue : function(){
53718         this.value = '';
53719         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53720         
53721     },
53722
53723     /**
53724      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53725      * will be displayed in the field.  If the value does not match the data value of an existing item,
53726      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53727      * Otherwise the field will be blank (although the value will still be set).
53728      * @param {String} value The value to match
53729      */
53730     setValue : function(v){
53731         var d = this.el.dom;
53732         for (var i =0; i < d.options.length;i++) {
53733             if (v == d.options[i].value) {
53734                 d.selectedIndex = i;
53735                 this.value = v;
53736                 return;
53737             }
53738         }
53739         this.clearValue();
53740     },
53741     /**
53742      * @property {Object} the last set data for the element
53743      */
53744     
53745     lastData : false,
53746     /**
53747      * Sets the value of the field based on a object which is related to the record format for the store.
53748      * @param {Object} value the value to set as. or false on reset?
53749      */
53750     setFromData : function(o){
53751         Roo.log('setfrom data?');
53752          
53753         
53754         
53755     },
53756     // private
53757     reset : function(){
53758         this.clearValue();
53759     },
53760     // private
53761     findRecord : function(prop, value){
53762         
53763         return false;
53764     
53765         var record;
53766         if(this.store.getCount() > 0){
53767             this.store.each(function(r){
53768                 if(r.data[prop] == value){
53769                     record = r;
53770                     return false;
53771                 }
53772                 return true;
53773             });
53774         }
53775         return record;
53776     },
53777     
53778     getName: function()
53779     {
53780         // returns hidden if it's set..
53781         if (!this.rendered) {return ''};
53782         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53783         
53784     },
53785      
53786
53787     
53788
53789     // private
53790     onEmptyResults : function(){
53791         Roo.log('empty results');
53792         //this.collapse();
53793     },
53794
53795     /**
53796      * Returns true if the dropdown list is expanded, else false.
53797      */
53798     isExpanded : function(){
53799         return false;
53800     },
53801
53802     /**
53803      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53804      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53805      * @param {String} value The data value of the item to select
53806      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53807      * selected item if it is not currently in view (defaults to true)
53808      * @return {Boolean} True if the value matched an item in the list, else false
53809      */
53810     selectByValue : function(v, scrollIntoView){
53811         Roo.log('select By Value');
53812         return false;
53813     
53814         if(v !== undefined && v !== null){
53815             var r = this.findRecord(this.valueField || this.displayField, v);
53816             if(r){
53817                 this.select(this.store.indexOf(r), scrollIntoView);
53818                 return true;
53819             }
53820         }
53821         return false;
53822     },
53823
53824     /**
53825      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53826      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53827      * @param {Number} index The zero-based index of the list item to select
53828      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53829      * selected item if it is not currently in view (defaults to true)
53830      */
53831     select : function(index, scrollIntoView){
53832         Roo.log('select ');
53833         return  ;
53834         
53835         this.selectedIndex = index;
53836         this.view.select(index);
53837         if(scrollIntoView !== false){
53838             var el = this.view.getNode(index);
53839             if(el){
53840                 this.innerList.scrollChildIntoView(el, false);
53841             }
53842         }
53843     },
53844
53845       
53846
53847     // private
53848     validateBlur : function(){
53849         
53850         return;
53851         
53852     },
53853
53854     // private
53855     initQuery : function(){
53856         this.doQuery(this.getRawValue());
53857     },
53858
53859     // private
53860     doForce : function(){
53861         if(this.el.dom.value.length > 0){
53862             this.el.dom.value =
53863                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53864              
53865         }
53866     },
53867
53868     /**
53869      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53870      * query allowing the query action to be canceled if needed.
53871      * @param {String} query The SQL query to execute
53872      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53873      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53874      * saved in the current store (defaults to false)
53875      */
53876     doQuery : function(q, forceAll){
53877         
53878         Roo.log('doQuery?');
53879         if(q === undefined || q === null){
53880             q = '';
53881         }
53882         var qe = {
53883             query: q,
53884             forceAll: forceAll,
53885             combo: this,
53886             cancel:false
53887         };
53888         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53889             return false;
53890         }
53891         q = qe.query;
53892         forceAll = qe.forceAll;
53893         if(forceAll === true || (q.length >= this.minChars)){
53894             if(this.lastQuery != q || this.alwaysQuery){
53895                 this.lastQuery = q;
53896                 if(this.mode == 'local'){
53897                     this.selectedIndex = -1;
53898                     if(forceAll){
53899                         this.store.clearFilter();
53900                     }else{
53901                         this.store.filter(this.displayField, q);
53902                     }
53903                     this.onLoad();
53904                 }else{
53905                     this.store.baseParams[this.queryParam] = q;
53906                     this.store.load({
53907                         params: this.getParams(q)
53908                     });
53909                     this.expand();
53910                 }
53911             }else{
53912                 this.selectedIndex = -1;
53913                 this.onLoad();   
53914             }
53915         }
53916     },
53917
53918     // private
53919     getParams : function(q){
53920         var p = {};
53921         //p[this.queryParam] = q;
53922         if(this.pageSize){
53923             p.start = 0;
53924             p.limit = this.pageSize;
53925         }
53926         return p;
53927     },
53928
53929     /**
53930      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53931      */
53932     collapse : function(){
53933         
53934     },
53935
53936     // private
53937     collapseIf : function(e){
53938         
53939     },
53940
53941     /**
53942      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53943      */
53944     expand : function(){
53945         
53946     } ,
53947
53948     // private
53949      
53950
53951     /** 
53952     * @cfg {Boolean} grow 
53953     * @hide 
53954     */
53955     /** 
53956     * @cfg {Number} growMin 
53957     * @hide 
53958     */
53959     /** 
53960     * @cfg {Number} growMax 
53961     * @hide 
53962     */
53963     /**
53964      * @hide
53965      * @method autoSize
53966      */
53967     
53968     setWidth : function()
53969     {
53970         
53971     },
53972     getResizeEl : function(){
53973         return this.el;
53974     }
53975 });//<script type="text/javasscript">
53976  
53977
53978 /**
53979  * @class Roo.DDView
53980  * A DnD enabled version of Roo.View.
53981  * @param {Element/String} container The Element in which to create the View.
53982  * @param {String} tpl The template string used to create the markup for each element of the View
53983  * @param {Object} config The configuration properties. These include all the config options of
53984  * {@link Roo.View} plus some specific to this class.<br>
53985  * <p>
53986  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53987  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53988  * <p>
53989  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53990 .x-view-drag-insert-above {
53991         border-top:1px dotted #3366cc;
53992 }
53993 .x-view-drag-insert-below {
53994         border-bottom:1px dotted #3366cc;
53995 }
53996 </code></pre>
53997  * 
53998  */
53999  
54000 Roo.DDView = function(container, tpl, config) {
54001     Roo.DDView.superclass.constructor.apply(this, arguments);
54002     this.getEl().setStyle("outline", "0px none");
54003     this.getEl().unselectable();
54004     if (this.dragGroup) {
54005         this.setDraggable(this.dragGroup.split(","));
54006     }
54007     if (this.dropGroup) {
54008         this.setDroppable(this.dropGroup.split(","));
54009     }
54010     if (this.deletable) {
54011         this.setDeletable();
54012     }
54013     this.isDirtyFlag = false;
54014         this.addEvents({
54015                 "drop" : true
54016         });
54017 };
54018
54019 Roo.extend(Roo.DDView, Roo.View, {
54020 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54021 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54022 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54023 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54024
54025         isFormField: true,
54026
54027         reset: Roo.emptyFn,
54028         
54029         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54030
54031         validate: function() {
54032                 return true;
54033         },
54034         
54035         destroy: function() {
54036                 this.purgeListeners();
54037                 this.getEl.removeAllListeners();
54038                 this.getEl().remove();
54039                 if (this.dragZone) {
54040                         if (this.dragZone.destroy) {
54041                                 this.dragZone.destroy();
54042                         }
54043                 }
54044                 if (this.dropZone) {
54045                         if (this.dropZone.destroy) {
54046                                 this.dropZone.destroy();
54047                         }
54048                 }
54049         },
54050
54051 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54052         getName: function() {
54053                 return this.name;
54054         },
54055
54056 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54057         setValue: function(v) {
54058                 if (!this.store) {
54059                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54060                 }
54061                 var data = {};
54062                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54063                 this.store.proxy = new Roo.data.MemoryProxy(data);
54064                 this.store.load();
54065         },
54066
54067 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54068         getValue: function() {
54069                 var result = '(';
54070                 this.store.each(function(rec) {
54071                         result += rec.id + ',';
54072                 });
54073                 return result.substr(0, result.length - 1) + ')';
54074         },
54075         
54076         getIds: function() {
54077                 var i = 0, result = new Array(this.store.getCount());
54078                 this.store.each(function(rec) {
54079                         result[i++] = rec.id;
54080                 });
54081                 return result;
54082         },
54083         
54084         isDirty: function() {
54085                 return this.isDirtyFlag;
54086         },
54087
54088 /**
54089  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54090  *      whole Element becomes the target, and this causes the drop gesture to append.
54091  */
54092     getTargetFromEvent : function(e) {
54093                 var target = e.getTarget();
54094                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54095                 target = target.parentNode;
54096                 }
54097                 if (!target) {
54098                         target = this.el.dom.lastChild || this.el.dom;
54099                 }
54100                 return target;
54101     },
54102
54103 /**
54104  *      Create the drag data which consists of an object which has the property "ddel" as
54105  *      the drag proxy element. 
54106  */
54107     getDragData : function(e) {
54108         var target = this.findItemFromChild(e.getTarget());
54109                 if(target) {
54110                         this.handleSelection(e);
54111                         var selNodes = this.getSelectedNodes();
54112             var dragData = {
54113                 source: this,
54114                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54115                 nodes: selNodes,
54116                 records: []
54117                         };
54118                         var selectedIndices = this.getSelectedIndexes();
54119                         for (var i = 0; i < selectedIndices.length; i++) {
54120                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54121                         }
54122                         if (selNodes.length == 1) {
54123                                 dragData.ddel = target.cloneNode(true); // the div element
54124                         } else {
54125                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54126                                 div.className = 'multi-proxy';
54127                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54128                                         div.appendChild(selNodes[i].cloneNode(true));
54129                                 }
54130                                 dragData.ddel = div;
54131                         }
54132             //console.log(dragData)
54133             //console.log(dragData.ddel.innerHTML)
54134                         return dragData;
54135                 }
54136         //console.log('nodragData')
54137                 return false;
54138     },
54139     
54140 /**     Specify to which ddGroup items in this DDView may be dragged. */
54141     setDraggable: function(ddGroup) {
54142         if (ddGroup instanceof Array) {
54143                 Roo.each(ddGroup, this.setDraggable, this);
54144                 return;
54145         }
54146         if (this.dragZone) {
54147                 this.dragZone.addToGroup(ddGroup);
54148         } else {
54149                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54150                                 containerScroll: true,
54151                                 ddGroup: ddGroup 
54152
54153                         });
54154 //                      Draggability implies selection. DragZone's mousedown selects the element.
54155                         if (!this.multiSelect) { this.singleSelect = true; }
54156
54157 //                      Wire the DragZone's handlers up to methods in *this*
54158                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54159                 }
54160     },
54161
54162 /**     Specify from which ddGroup this DDView accepts drops. */
54163     setDroppable: function(ddGroup) {
54164         if (ddGroup instanceof Array) {
54165                 Roo.each(ddGroup, this.setDroppable, this);
54166                 return;
54167         }
54168         if (this.dropZone) {
54169                 this.dropZone.addToGroup(ddGroup);
54170         } else {
54171                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54172                                 containerScroll: true,
54173                                 ddGroup: ddGroup
54174                         });
54175
54176 //                      Wire the DropZone's handlers up to methods in *this*
54177                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54178                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54179                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54180                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54181                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54182                 }
54183     },
54184
54185 /**     Decide whether to drop above or below a View node. */
54186     getDropPoint : function(e, n, dd){
54187         if (n == this.el.dom) { return "above"; }
54188                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54189                 var c = t + (b - t) / 2;
54190                 var y = Roo.lib.Event.getPageY(e);
54191                 if(y <= c) {
54192                         return "above";
54193                 }else{
54194                         return "below";
54195                 }
54196     },
54197
54198     onNodeEnter : function(n, dd, e, data){
54199                 return false;
54200     },
54201     
54202     onNodeOver : function(n, dd, e, data){
54203                 var pt = this.getDropPoint(e, n, dd);
54204                 // set the insert point style on the target node
54205                 var dragElClass = this.dropNotAllowed;
54206                 if (pt) {
54207                         var targetElClass;
54208                         if (pt == "above"){
54209                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54210                                 targetElClass = "x-view-drag-insert-above";
54211                         } else {
54212                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54213                                 targetElClass = "x-view-drag-insert-below";
54214                         }
54215                         if (this.lastInsertClass != targetElClass){
54216                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54217                                 this.lastInsertClass = targetElClass;
54218                         }
54219                 }
54220                 return dragElClass;
54221         },
54222
54223     onNodeOut : function(n, dd, e, data){
54224                 this.removeDropIndicators(n);
54225     },
54226
54227     onNodeDrop : function(n, dd, e, data){
54228         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54229                 return false;
54230         }
54231         var pt = this.getDropPoint(e, n, dd);
54232                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54233                 if (pt == "below") { insertAt++; }
54234                 for (var i = 0; i < data.records.length; i++) {
54235                         var r = data.records[i];
54236                         var dup = this.store.getById(r.id);
54237                         if (dup && (dd != this.dragZone)) {
54238                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54239                         } else {
54240                                 if (data.copy) {
54241                                         this.store.insert(insertAt++, r.copy());
54242                                 } else {
54243                                         data.source.isDirtyFlag = true;
54244                                         r.store.remove(r);
54245                                         this.store.insert(insertAt++, r);
54246                                 }
54247                                 this.isDirtyFlag = true;
54248                         }
54249                 }
54250                 this.dragZone.cachedTarget = null;
54251                 return true;
54252     },
54253
54254     removeDropIndicators : function(n){
54255                 if(n){
54256                         Roo.fly(n).removeClass([
54257                                 "x-view-drag-insert-above",
54258                                 "x-view-drag-insert-below"]);
54259                         this.lastInsertClass = "_noclass";
54260                 }
54261     },
54262
54263 /**
54264  *      Utility method. Add a delete option to the DDView's context menu.
54265  *      @param {String} imageUrl The URL of the "delete" icon image.
54266  */
54267         setDeletable: function(imageUrl) {
54268                 if (!this.singleSelect && !this.multiSelect) {
54269                         this.singleSelect = true;
54270                 }
54271                 var c = this.getContextMenu();
54272                 this.contextMenu.on("itemclick", function(item) {
54273                         switch (item.id) {
54274                                 case "delete":
54275                                         this.remove(this.getSelectedIndexes());
54276                                         break;
54277                         }
54278                 }, this);
54279                 this.contextMenu.add({
54280                         icon: imageUrl,
54281                         id: "delete",
54282                         text: 'Delete'
54283                 });
54284         },
54285         
54286 /**     Return the context menu for this DDView. */
54287         getContextMenu: function() {
54288                 if (!this.contextMenu) {
54289 //                      Create the View's context menu
54290                         this.contextMenu = new Roo.menu.Menu({
54291                                 id: this.id + "-contextmenu"
54292                         });
54293                         this.el.on("contextmenu", this.showContextMenu, this);
54294                 }
54295                 return this.contextMenu;
54296         },
54297         
54298         disableContextMenu: function() {
54299                 if (this.contextMenu) {
54300                         this.el.un("contextmenu", this.showContextMenu, this);
54301                 }
54302         },
54303
54304         showContextMenu: function(e, item) {
54305         item = this.findItemFromChild(e.getTarget());
54306                 if (item) {
54307                         e.stopEvent();
54308                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54309                         this.contextMenu.showAt(e.getXY());
54310             }
54311     },
54312
54313 /**
54314  *      Remove {@link Roo.data.Record}s at the specified indices.
54315  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54316  */
54317     remove: function(selectedIndices) {
54318                 selectedIndices = [].concat(selectedIndices);
54319                 for (var i = 0; i < selectedIndices.length; i++) {
54320                         var rec = this.store.getAt(selectedIndices[i]);
54321                         this.store.remove(rec);
54322                 }
54323     },
54324
54325 /**
54326  *      Double click fires the event, but also, if this is draggable, and there is only one other
54327  *      related DropZone, it transfers the selected node.
54328  */
54329     onDblClick : function(e){
54330         var item = this.findItemFromChild(e.getTarget());
54331         if(item){
54332             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54333                 return false;
54334             }
54335             if (this.dragGroup) {
54336                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54337                     while (targets.indexOf(this.dropZone) > -1) {
54338                             targets.remove(this.dropZone);
54339                                 }
54340                     if (targets.length == 1) {
54341                                         this.dragZone.cachedTarget = null;
54342                         var el = Roo.get(targets[0].getEl());
54343                         var box = el.getBox(true);
54344                         targets[0].onNodeDrop(el.dom, {
54345                                 target: el.dom,
54346                                 xy: [box.x, box.y + box.height - 1]
54347                         }, null, this.getDragData(e));
54348                     }
54349                 }
54350         }
54351     },
54352     
54353     handleSelection: function(e) {
54354                 this.dragZone.cachedTarget = null;
54355         var item = this.findItemFromChild(e.getTarget());
54356         if (!item) {
54357                 this.clearSelections(true);
54358                 return;
54359         }
54360                 if (item && (this.multiSelect || this.singleSelect)){
54361                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54362                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54363                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54364                                 this.unselect(item);
54365                         } else {
54366                                 this.select(item, this.multiSelect && e.ctrlKey);
54367                                 this.lastSelection = item;
54368                         }
54369                 }
54370     },
54371
54372     onItemClick : function(item, index, e){
54373                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54374                         return false;
54375                 }
54376                 return true;
54377     },
54378
54379     unselect : function(nodeInfo, suppressEvent){
54380                 var node = this.getNode(nodeInfo);
54381                 if(node && this.isSelected(node)){
54382                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54383                                 Roo.fly(node).removeClass(this.selectedClass);
54384                                 this.selections.remove(node);
54385                                 if(!suppressEvent){
54386                                         this.fireEvent("selectionchange", this, this.selections);
54387                                 }
54388                         }
54389                 }
54390     }
54391 });
54392 /*
54393  * Based on:
54394  * Ext JS Library 1.1.1
54395  * Copyright(c) 2006-2007, Ext JS, LLC.
54396  *
54397  * Originally Released Under LGPL - original licence link has changed is not relivant.
54398  *
54399  * Fork - LGPL
54400  * <script type="text/javascript">
54401  */
54402  
54403 /**
54404  * @class Roo.LayoutManager
54405  * @extends Roo.util.Observable
54406  * Base class for layout managers.
54407  */
54408 Roo.LayoutManager = function(container, config){
54409     Roo.LayoutManager.superclass.constructor.call(this);
54410     this.el = Roo.get(container);
54411     // ie scrollbar fix
54412     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54413         document.body.scroll = "no";
54414     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54415         this.el.position('relative');
54416     }
54417     this.id = this.el.id;
54418     this.el.addClass("x-layout-container");
54419     /** false to disable window resize monitoring @type Boolean */
54420     this.monitorWindowResize = true;
54421     this.regions = {};
54422     this.addEvents({
54423         /**
54424          * @event layout
54425          * Fires when a layout is performed. 
54426          * @param {Roo.LayoutManager} this
54427          */
54428         "layout" : true,
54429         /**
54430          * @event regionresized
54431          * Fires when the user resizes a region. 
54432          * @param {Roo.LayoutRegion} region The resized region
54433          * @param {Number} newSize The new size (width for east/west, height for north/south)
54434          */
54435         "regionresized" : true,
54436         /**
54437          * @event regioncollapsed
54438          * Fires when a region is collapsed. 
54439          * @param {Roo.LayoutRegion} region The collapsed region
54440          */
54441         "regioncollapsed" : true,
54442         /**
54443          * @event regionexpanded
54444          * Fires when a region is expanded.  
54445          * @param {Roo.LayoutRegion} region The expanded region
54446          */
54447         "regionexpanded" : true
54448     });
54449     this.updating = false;
54450     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54451 };
54452
54453 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54454     /**
54455      * Returns true if this layout is currently being updated
54456      * @return {Boolean}
54457      */
54458     isUpdating : function(){
54459         return this.updating; 
54460     },
54461     
54462     /**
54463      * Suspend the LayoutManager from doing auto-layouts while
54464      * making multiple add or remove calls
54465      */
54466     beginUpdate : function(){
54467         this.updating = true;    
54468     },
54469     
54470     /**
54471      * Restore auto-layouts and optionally disable the manager from performing a layout
54472      * @param {Boolean} noLayout true to disable a layout update 
54473      */
54474     endUpdate : function(noLayout){
54475         this.updating = false;
54476         if(!noLayout){
54477             this.layout();
54478         }    
54479     },
54480     
54481     layout: function(){
54482         
54483     },
54484     
54485     onRegionResized : function(region, newSize){
54486         this.fireEvent("regionresized", region, newSize);
54487         this.layout();
54488     },
54489     
54490     onRegionCollapsed : function(region){
54491         this.fireEvent("regioncollapsed", region);
54492     },
54493     
54494     onRegionExpanded : function(region){
54495         this.fireEvent("regionexpanded", region);
54496     },
54497         
54498     /**
54499      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54500      * performs box-model adjustments.
54501      * @return {Object} The size as an object {width: (the width), height: (the height)}
54502      */
54503     getViewSize : function(){
54504         var size;
54505         if(this.el.dom != document.body){
54506             size = this.el.getSize();
54507         }else{
54508             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54509         }
54510         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54511         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54512         return size;
54513     },
54514     
54515     /**
54516      * Returns the Element this layout is bound to.
54517      * @return {Roo.Element}
54518      */
54519     getEl : function(){
54520         return this.el;
54521     },
54522     
54523     /**
54524      * Returns the specified region.
54525      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54526      * @return {Roo.LayoutRegion}
54527      */
54528     getRegion : function(target){
54529         return this.regions[target.toLowerCase()];
54530     },
54531     
54532     onWindowResize : function(){
54533         if(this.monitorWindowResize){
54534             this.layout();
54535         }
54536     }
54537 });/*
54538  * Based on:
54539  * Ext JS Library 1.1.1
54540  * Copyright(c) 2006-2007, Ext JS, LLC.
54541  *
54542  * Originally Released Under LGPL - original licence link has changed is not relivant.
54543  *
54544  * Fork - LGPL
54545  * <script type="text/javascript">
54546  */
54547 /**
54548  * @class Roo.BorderLayout
54549  * @extends Roo.LayoutManager
54550  * @children Roo.ContentPanel
54551  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54552  * please see: <br><br>
54553  * <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>
54554  * <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>
54555  * Example:
54556  <pre><code>
54557  var layout = new Roo.BorderLayout(document.body, {
54558     north: {
54559         initialSize: 25,
54560         titlebar: false
54561     },
54562     west: {
54563         split:true,
54564         initialSize: 200,
54565         minSize: 175,
54566         maxSize: 400,
54567         titlebar: true,
54568         collapsible: true
54569     },
54570     east: {
54571         split:true,
54572         initialSize: 202,
54573         minSize: 175,
54574         maxSize: 400,
54575         titlebar: true,
54576         collapsible: true
54577     },
54578     south: {
54579         split:true,
54580         initialSize: 100,
54581         minSize: 100,
54582         maxSize: 200,
54583         titlebar: true,
54584         collapsible: true
54585     },
54586     center: {
54587         titlebar: true,
54588         autoScroll:true,
54589         resizeTabs: true,
54590         minTabWidth: 50,
54591         preferredTabWidth: 150
54592     }
54593 });
54594
54595 // shorthand
54596 var CP = Roo.ContentPanel;
54597
54598 layout.beginUpdate();
54599 layout.add("north", new CP("north", "North"));
54600 layout.add("south", new CP("south", {title: "South", closable: true}));
54601 layout.add("west", new CP("west", {title: "West"}));
54602 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54603 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54604 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54605 layout.getRegion("center").showPanel("center1");
54606 layout.endUpdate();
54607 </code></pre>
54608
54609 <b>The container the layout is rendered into can be either the body element or any other element.
54610 If it is not the body element, the container needs to either be an absolute positioned element,
54611 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54612 the container size if it is not the body element.</b>
54613
54614 * @constructor
54615 * Create a new BorderLayout
54616 * @param {String/HTMLElement/Element} container The container this layout is bound to
54617 * @param {Object} config Configuration options
54618  */
54619 Roo.BorderLayout = function(container, config){
54620     config = config || {};
54621     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54622     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54623     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54624         var target = this.factory.validRegions[i];
54625         if(config[target]){
54626             this.addRegion(target, config[target]);
54627         }
54628     }
54629 };
54630
54631 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54632         
54633         /**
54634          * @cfg {Roo.LayoutRegion} east
54635          */
54636         /**
54637          * @cfg {Roo.LayoutRegion} west
54638          */
54639         /**
54640          * @cfg {Roo.LayoutRegion} north
54641          */
54642         /**
54643          * @cfg {Roo.LayoutRegion} south
54644          */
54645         /**
54646          * @cfg {Roo.LayoutRegion} center
54647          */
54648     /**
54649      * Creates and adds a new region if it doesn't already exist.
54650      * @param {String} target The target region key (north, south, east, west or center).
54651      * @param {Object} config The regions config object
54652      * @return {BorderLayoutRegion} The new region
54653      */
54654     addRegion : function(target, config){
54655         if(!this.regions[target]){
54656             var r = this.factory.create(target, this, config);
54657             this.bindRegion(target, r);
54658         }
54659         return this.regions[target];
54660     },
54661
54662     // private (kinda)
54663     bindRegion : function(name, r){
54664         this.regions[name] = r;
54665         r.on("visibilitychange", this.layout, this);
54666         r.on("paneladded", this.layout, this);
54667         r.on("panelremoved", this.layout, this);
54668         r.on("invalidated", this.layout, this);
54669         r.on("resized", this.onRegionResized, this);
54670         r.on("collapsed", this.onRegionCollapsed, this);
54671         r.on("expanded", this.onRegionExpanded, this);
54672     },
54673
54674     /**
54675      * Performs a layout update.
54676      */
54677     layout : function(){
54678         if(this.updating) {
54679             return;
54680         }
54681         var size = this.getViewSize();
54682         var w = size.width;
54683         var h = size.height;
54684         var centerW = w;
54685         var centerH = h;
54686         var centerY = 0;
54687         var centerX = 0;
54688         //var x = 0, y = 0;
54689
54690         var rs = this.regions;
54691         var north = rs["north"];
54692         var south = rs["south"]; 
54693         var west = rs["west"];
54694         var east = rs["east"];
54695         var center = rs["center"];
54696         //if(this.hideOnLayout){ // not supported anymore
54697             //c.el.setStyle("display", "none");
54698         //}
54699         if(north && north.isVisible()){
54700             var b = north.getBox();
54701             var m = north.getMargins();
54702             b.width = w - (m.left+m.right);
54703             b.x = m.left;
54704             b.y = m.top;
54705             centerY = b.height + b.y + m.bottom;
54706             centerH -= centerY;
54707             north.updateBox(this.safeBox(b));
54708         }
54709         if(south && south.isVisible()){
54710             var b = south.getBox();
54711             var m = south.getMargins();
54712             b.width = w - (m.left+m.right);
54713             b.x = m.left;
54714             var totalHeight = (b.height + m.top + m.bottom);
54715             b.y = h - totalHeight + m.top;
54716             centerH -= totalHeight;
54717             south.updateBox(this.safeBox(b));
54718         }
54719         if(west && west.isVisible()){
54720             var b = west.getBox();
54721             var m = west.getMargins();
54722             b.height = centerH - (m.top+m.bottom);
54723             b.x = m.left;
54724             b.y = centerY + m.top;
54725             var totalWidth = (b.width + m.left + m.right);
54726             centerX += totalWidth;
54727             centerW -= totalWidth;
54728             west.updateBox(this.safeBox(b));
54729         }
54730         if(east && east.isVisible()){
54731             var b = east.getBox();
54732             var m = east.getMargins();
54733             b.height = centerH - (m.top+m.bottom);
54734             var totalWidth = (b.width + m.left + m.right);
54735             b.x = w - totalWidth + m.left;
54736             b.y = centerY + m.top;
54737             centerW -= totalWidth;
54738             east.updateBox(this.safeBox(b));
54739         }
54740         if(center){
54741             var m = center.getMargins();
54742             var centerBox = {
54743                 x: centerX + m.left,
54744                 y: centerY + m.top,
54745                 width: centerW - (m.left+m.right),
54746                 height: centerH - (m.top+m.bottom)
54747             };
54748             //if(this.hideOnLayout){
54749                 //center.el.setStyle("display", "block");
54750             //}
54751             center.updateBox(this.safeBox(centerBox));
54752         }
54753         this.el.repaint();
54754         this.fireEvent("layout", this);
54755     },
54756
54757     // private
54758     safeBox : function(box){
54759         box.width = Math.max(0, box.width);
54760         box.height = Math.max(0, box.height);
54761         return box;
54762     },
54763
54764     /**
54765      * Adds a ContentPanel (or subclass) to this layout.
54766      * @param {String} target The target region key (north, south, east, west or center).
54767      * @param {Roo.ContentPanel} panel The panel to add
54768      * @return {Roo.ContentPanel} The added panel
54769      */
54770     add : function(target, panel){
54771          
54772         target = target.toLowerCase();
54773         return this.regions[target].add(panel);
54774     },
54775
54776     /**
54777      * Remove a ContentPanel (or subclass) to this layout.
54778      * @param {String} target The target region key (north, south, east, west or center).
54779      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54780      * @return {Roo.ContentPanel} The removed panel
54781      */
54782     remove : function(target, panel){
54783         target = target.toLowerCase();
54784         return this.regions[target].remove(panel);
54785     },
54786
54787     /**
54788      * Searches all regions for a panel with the specified id
54789      * @param {String} panelId
54790      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54791      */
54792     findPanel : function(panelId){
54793         var rs = this.regions;
54794         for(var target in rs){
54795             if(typeof rs[target] != "function"){
54796                 var p = rs[target].getPanel(panelId);
54797                 if(p){
54798                     return p;
54799                 }
54800             }
54801         }
54802         return null;
54803     },
54804
54805     /**
54806      * Searches all regions for a panel with the specified id and activates (shows) it.
54807      * @param {String/ContentPanel} panelId The panels id or the panel itself
54808      * @return {Roo.ContentPanel} The shown panel or null
54809      */
54810     showPanel : function(panelId) {
54811       var rs = this.regions;
54812       for(var target in rs){
54813          var r = rs[target];
54814          if(typeof r != "function"){
54815             if(r.hasPanel(panelId)){
54816                return r.showPanel(panelId);
54817             }
54818          }
54819       }
54820       return null;
54821    },
54822
54823    /**
54824      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54825      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54826      */
54827     restoreState : function(provider){
54828         if(!provider){
54829             provider = Roo.state.Manager;
54830         }
54831         var sm = new Roo.LayoutStateManager();
54832         sm.init(this, provider);
54833     },
54834
54835     /**
54836      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54837      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54838      * a valid ContentPanel config object.  Example:
54839      * <pre><code>
54840 // Create the main layout
54841 var layout = new Roo.BorderLayout('main-ct', {
54842     west: {
54843         split:true,
54844         minSize: 175,
54845         titlebar: true
54846     },
54847     center: {
54848         title:'Components'
54849     }
54850 }, 'main-ct');
54851
54852 // Create and add multiple ContentPanels at once via configs
54853 layout.batchAdd({
54854    west: {
54855        id: 'source-files',
54856        autoCreate:true,
54857        title:'Ext Source Files',
54858        autoScroll:true,
54859        fitToFrame:true
54860    },
54861    center : {
54862        el: cview,
54863        autoScroll:true,
54864        fitToFrame:true,
54865        toolbar: tb,
54866        resizeEl:'cbody'
54867    }
54868 });
54869 </code></pre>
54870      * @param {Object} regions An object containing ContentPanel configs by region name
54871      */
54872     batchAdd : function(regions){
54873         this.beginUpdate();
54874         for(var rname in regions){
54875             var lr = this.regions[rname];
54876             if(lr){
54877                 this.addTypedPanels(lr, regions[rname]);
54878             }
54879         }
54880         this.endUpdate();
54881     },
54882
54883     // private
54884     addTypedPanels : function(lr, ps){
54885         if(typeof ps == 'string'){
54886             lr.add(new Roo.ContentPanel(ps));
54887         }
54888         else if(ps instanceof Array){
54889             for(var i =0, len = ps.length; i < len; i++){
54890                 this.addTypedPanels(lr, ps[i]);
54891             }
54892         }
54893         else if(!ps.events){ // raw config?
54894             var el = ps.el;
54895             delete ps.el; // prevent conflict
54896             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54897         }
54898         else {  // panel object assumed!
54899             lr.add(ps);
54900         }
54901     },
54902     /**
54903      * Adds a xtype elements to the layout.
54904      * <pre><code>
54905
54906 layout.addxtype({
54907        xtype : 'ContentPanel',
54908        region: 'west',
54909        items: [ .... ]
54910    }
54911 );
54912
54913 layout.addxtype({
54914         xtype : 'NestedLayoutPanel',
54915         region: 'west',
54916         layout: {
54917            center: { },
54918            west: { }   
54919         },
54920         items : [ ... list of content panels or nested layout panels.. ]
54921    }
54922 );
54923 </code></pre>
54924      * @param {Object} cfg Xtype definition of item to add.
54925      */
54926     addxtype : function(cfg)
54927     {
54928         // basically accepts a pannel...
54929         // can accept a layout region..!?!?
54930         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54931         
54932         if (!cfg.xtype.match(/Panel$/)) {
54933             return false;
54934         }
54935         var ret = false;
54936         
54937         if (typeof(cfg.region) == 'undefined') {
54938             Roo.log("Failed to add Panel, region was not set");
54939             Roo.log(cfg);
54940             return false;
54941         }
54942         var region = cfg.region;
54943         delete cfg.region;
54944         
54945           
54946         var xitems = [];
54947         if (cfg.items) {
54948             xitems = cfg.items;
54949             delete cfg.items;
54950         }
54951         var nb = false;
54952         
54953         switch(cfg.xtype) 
54954         {
54955             case 'ContentPanel':  // ContentPanel (el, cfg)
54956             case 'ScrollPanel':  // ContentPanel (el, cfg)
54957             case 'ViewPanel': 
54958                 if(cfg.autoCreate) {
54959                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54960                 } else {
54961                     var el = this.el.createChild();
54962                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54963                 }
54964                 
54965                 this.add(region, ret);
54966                 break;
54967             
54968             
54969             case 'TreePanel': // our new panel!
54970                 cfg.el = this.el.createChild();
54971                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54972                 this.add(region, ret);
54973                 break;
54974             
54975             case 'NestedLayoutPanel': 
54976                 // create a new Layout (which is  a Border Layout...
54977                 var el = this.el.createChild();
54978                 var clayout = cfg.layout;
54979                 delete cfg.layout;
54980                 clayout.items   = clayout.items  || [];
54981                 // replace this exitems with the clayout ones..
54982                 xitems = clayout.items;
54983                  
54984                 
54985                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54986                     cfg.background = false;
54987                 }
54988                 var layout = new Roo.BorderLayout(el, clayout);
54989                 
54990                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54991                 //console.log('adding nested layout panel '  + cfg.toSource());
54992                 this.add(region, ret);
54993                 nb = {}; /// find first...
54994                 break;
54995                 
54996             case 'GridPanel': 
54997             
54998                 // needs grid and region
54999                 
55000                 //var el = this.getRegion(region).el.createChild();
55001                 var el = this.el.createChild();
55002                 // create the grid first...
55003                 
55004                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55005                 delete cfg.grid;
55006                 if (region == 'center' && this.active ) {
55007                     cfg.background = false;
55008                 }
55009                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55010                 
55011                 this.add(region, ret);
55012                 if (cfg.background) {
55013                     ret.on('activate', function(gp) {
55014                         if (!gp.grid.rendered) {
55015                             gp.grid.render();
55016                         }
55017                     });
55018                 } else {
55019                     grid.render();
55020                 }
55021                 break;
55022            
55023            
55024            
55025                 
55026                 
55027                 
55028             default:
55029                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55030                     
55031                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55032                     this.add(region, ret);
55033                 } else {
55034                 
55035                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55036                     return null;
55037                 }
55038                 
55039              // GridPanel (grid, cfg)
55040             
55041         }
55042         this.beginUpdate();
55043         // add children..
55044         var region = '';
55045         var abn = {};
55046         Roo.each(xitems, function(i)  {
55047             region = nb && i.region ? i.region : false;
55048             
55049             var add = ret.addxtype(i);
55050            
55051             if (region) {
55052                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55053                 if (!i.background) {
55054                     abn[region] = nb[region] ;
55055                 }
55056             }
55057             
55058         });
55059         this.endUpdate();
55060
55061         // make the last non-background panel active..
55062         //if (nb) { Roo.log(abn); }
55063         if (nb) {
55064             
55065             for(var r in abn) {
55066                 region = this.getRegion(r);
55067                 if (region) {
55068                     // tried using nb[r], but it does not work..
55069                      
55070                     region.showPanel(abn[r]);
55071                    
55072                 }
55073             }
55074         }
55075         return ret;
55076         
55077     }
55078 });
55079
55080 /**
55081  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55082  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55083  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55084  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55085  * <pre><code>
55086 // shorthand
55087 var CP = Roo.ContentPanel;
55088
55089 var layout = Roo.BorderLayout.create({
55090     north: {
55091         initialSize: 25,
55092         titlebar: false,
55093         panels: [new CP("north", "North")]
55094     },
55095     west: {
55096         split:true,
55097         initialSize: 200,
55098         minSize: 175,
55099         maxSize: 400,
55100         titlebar: true,
55101         collapsible: true,
55102         panels: [new CP("west", {title: "West"})]
55103     },
55104     east: {
55105         split:true,
55106         initialSize: 202,
55107         minSize: 175,
55108         maxSize: 400,
55109         titlebar: true,
55110         collapsible: true,
55111         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55112     },
55113     south: {
55114         split:true,
55115         initialSize: 100,
55116         minSize: 100,
55117         maxSize: 200,
55118         titlebar: true,
55119         collapsible: true,
55120         panels: [new CP("south", {title: "South", closable: true})]
55121     },
55122     center: {
55123         titlebar: true,
55124         autoScroll:true,
55125         resizeTabs: true,
55126         minTabWidth: 50,
55127         preferredTabWidth: 150,
55128         panels: [
55129             new CP("center1", {title: "Close Me", closable: true}),
55130             new CP("center2", {title: "Center Panel", closable: false})
55131         ]
55132     }
55133 }, document.body);
55134
55135 layout.getRegion("center").showPanel("center1");
55136 </code></pre>
55137  * @param config
55138  * @param targetEl
55139  */
55140 Roo.BorderLayout.create = function(config, targetEl){
55141     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55142     layout.beginUpdate();
55143     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55144     for(var j = 0, jlen = regions.length; j < jlen; j++){
55145         var lr = regions[j];
55146         if(layout.regions[lr] && config[lr].panels){
55147             var r = layout.regions[lr];
55148             var ps = config[lr].panels;
55149             layout.addTypedPanels(r, ps);
55150         }
55151     }
55152     layout.endUpdate();
55153     return layout;
55154 };
55155
55156 // private
55157 Roo.BorderLayout.RegionFactory = {
55158     // private
55159     validRegions : ["north","south","east","west","center"],
55160
55161     // private
55162     create : function(target, mgr, config){
55163         target = target.toLowerCase();
55164         if(config.lightweight || config.basic){
55165             return new Roo.BasicLayoutRegion(mgr, config, target);
55166         }
55167         switch(target){
55168             case "north":
55169                 return new Roo.NorthLayoutRegion(mgr, config);
55170             case "south":
55171                 return new Roo.SouthLayoutRegion(mgr, config);
55172             case "east":
55173                 return new Roo.EastLayoutRegion(mgr, config);
55174             case "west":
55175                 return new Roo.WestLayoutRegion(mgr, config);
55176             case "center":
55177                 return new Roo.CenterLayoutRegion(mgr, config);
55178         }
55179         throw 'Layout region "'+target+'" not supported.';
55180     }
55181 };/*
55182  * Based on:
55183  * Ext JS Library 1.1.1
55184  * Copyright(c) 2006-2007, Ext JS, LLC.
55185  *
55186  * Originally Released Under LGPL - original licence link has changed is not relivant.
55187  *
55188  * Fork - LGPL
55189  * <script type="text/javascript">
55190  */
55191  
55192 /**
55193  * @class Roo.BasicLayoutRegion
55194  * @extends Roo.util.Observable
55195  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55196  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55197  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55198  */
55199 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55200     this.mgr = mgr;
55201     this.position  = pos;
55202     this.events = {
55203         /**
55204          * @scope Roo.BasicLayoutRegion
55205          */
55206         
55207         /**
55208          * @event beforeremove
55209          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55210          * @param {Roo.LayoutRegion} this
55211          * @param {Roo.ContentPanel} panel The panel
55212          * @param {Object} e The cancel event object
55213          */
55214         "beforeremove" : true,
55215         /**
55216          * @event invalidated
55217          * Fires when the layout for this region is changed.
55218          * @param {Roo.LayoutRegion} this
55219          */
55220         "invalidated" : true,
55221         /**
55222          * @event visibilitychange
55223          * Fires when this region is shown or hidden 
55224          * @param {Roo.LayoutRegion} this
55225          * @param {Boolean} visibility true or false
55226          */
55227         "visibilitychange" : true,
55228         /**
55229          * @event paneladded
55230          * Fires when a panel is added. 
55231          * @param {Roo.LayoutRegion} this
55232          * @param {Roo.ContentPanel} panel The panel
55233          */
55234         "paneladded" : true,
55235         /**
55236          * @event panelremoved
55237          * Fires when a panel is removed. 
55238          * @param {Roo.LayoutRegion} this
55239          * @param {Roo.ContentPanel} panel The panel
55240          */
55241         "panelremoved" : true,
55242         /**
55243          * @event beforecollapse
55244          * Fires when this region before collapse.
55245          * @param {Roo.LayoutRegion} this
55246          */
55247         "beforecollapse" : true,
55248         /**
55249          * @event collapsed
55250          * Fires when this region is collapsed.
55251          * @param {Roo.LayoutRegion} this
55252          */
55253         "collapsed" : true,
55254         /**
55255          * @event expanded
55256          * Fires when this region is expanded.
55257          * @param {Roo.LayoutRegion} this
55258          */
55259         "expanded" : true,
55260         /**
55261          * @event slideshow
55262          * Fires when this region is slid into view.
55263          * @param {Roo.LayoutRegion} this
55264          */
55265         "slideshow" : true,
55266         /**
55267          * @event slidehide
55268          * Fires when this region slides out of view. 
55269          * @param {Roo.LayoutRegion} this
55270          */
55271         "slidehide" : true,
55272         /**
55273          * @event panelactivated
55274          * Fires when a panel is activated. 
55275          * @param {Roo.LayoutRegion} this
55276          * @param {Roo.ContentPanel} panel The activated panel
55277          */
55278         "panelactivated" : true,
55279         /**
55280          * @event resized
55281          * Fires when the user resizes this region. 
55282          * @param {Roo.LayoutRegion} this
55283          * @param {Number} newSize The new size (width for east/west, height for north/south)
55284          */
55285         "resized" : true
55286     };
55287     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55288     this.panels = new Roo.util.MixedCollection();
55289     this.panels.getKey = this.getPanelId.createDelegate(this);
55290     this.box = null;
55291     this.activePanel = null;
55292     // ensure listeners are added...
55293     
55294     if (config.listeners || config.events) {
55295         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55296             listeners : config.listeners || {},
55297             events : config.events || {}
55298         });
55299     }
55300     
55301     if(skipConfig !== true){
55302         this.applyConfig(config);
55303     }
55304 };
55305
55306 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55307     getPanelId : function(p){
55308         return p.getId();
55309     },
55310     
55311     applyConfig : function(config){
55312         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55313         this.config = config;
55314         
55315     },
55316     
55317     /**
55318      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55319      * the width, for horizontal (north, south) the height.
55320      * @param {Number} newSize The new width or height
55321      */
55322     resizeTo : function(newSize){
55323         var el = this.el ? this.el :
55324                  (this.activePanel ? this.activePanel.getEl() : null);
55325         if(el){
55326             switch(this.position){
55327                 case "east":
55328                 case "west":
55329                     el.setWidth(newSize);
55330                     this.fireEvent("resized", this, newSize);
55331                 break;
55332                 case "north":
55333                 case "south":
55334                     el.setHeight(newSize);
55335                     this.fireEvent("resized", this, newSize);
55336                 break;                
55337             }
55338         }
55339     },
55340     
55341     getBox : function(){
55342         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55343     },
55344     
55345     getMargins : function(){
55346         return this.margins;
55347     },
55348     
55349     updateBox : function(box){
55350         this.box = box;
55351         var el = this.activePanel.getEl();
55352         el.dom.style.left = box.x + "px";
55353         el.dom.style.top = box.y + "px";
55354         this.activePanel.setSize(box.width, box.height);
55355     },
55356     
55357     /**
55358      * Returns the container element for this region.
55359      * @return {Roo.Element}
55360      */
55361     getEl : function(){
55362         return this.activePanel;
55363     },
55364     
55365     /**
55366      * Returns true if this region is currently visible.
55367      * @return {Boolean}
55368      */
55369     isVisible : function(){
55370         return this.activePanel ? true : false;
55371     },
55372     
55373     setActivePanel : function(panel){
55374         panel = this.getPanel(panel);
55375         if(this.activePanel && this.activePanel != panel){
55376             this.activePanel.setActiveState(false);
55377             this.activePanel.getEl().setLeftTop(-10000,-10000);
55378         }
55379         this.activePanel = panel;
55380         panel.setActiveState(true);
55381         if(this.box){
55382             panel.setSize(this.box.width, this.box.height);
55383         }
55384         this.fireEvent("panelactivated", this, panel);
55385         this.fireEvent("invalidated");
55386     },
55387     
55388     /**
55389      * Show the specified panel.
55390      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55391      * @return {Roo.ContentPanel} The shown panel or null
55392      */
55393     showPanel : function(panel){
55394         if(panel = this.getPanel(panel)){
55395             this.setActivePanel(panel);
55396         }
55397         return panel;
55398     },
55399     
55400     /**
55401      * Get the active panel for this region.
55402      * @return {Roo.ContentPanel} The active panel or null
55403      */
55404     getActivePanel : function(){
55405         return this.activePanel;
55406     },
55407     
55408     /**
55409      * Add the passed ContentPanel(s)
55410      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55411      * @return {Roo.ContentPanel} The panel added (if only one was added)
55412      */
55413     add : function(panel){
55414         if(arguments.length > 1){
55415             for(var i = 0, len = arguments.length; i < len; i++) {
55416                 this.add(arguments[i]);
55417             }
55418             return null;
55419         }
55420         if(this.hasPanel(panel)){
55421             this.showPanel(panel);
55422             return panel;
55423         }
55424         var el = panel.getEl();
55425         if(el.dom.parentNode != this.mgr.el.dom){
55426             this.mgr.el.dom.appendChild(el.dom);
55427         }
55428         if(panel.setRegion){
55429             panel.setRegion(this);
55430         }
55431         this.panels.add(panel);
55432         el.setStyle("position", "absolute");
55433         if(!panel.background){
55434             this.setActivePanel(panel);
55435             if(this.config.initialSize && this.panels.getCount()==1){
55436                 this.resizeTo(this.config.initialSize);
55437             }
55438         }
55439         this.fireEvent("paneladded", this, panel);
55440         return panel;
55441     },
55442     
55443     /**
55444      * Returns true if the panel is in this region.
55445      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55446      * @return {Boolean}
55447      */
55448     hasPanel : function(panel){
55449         if(typeof panel == "object"){ // must be panel obj
55450             panel = panel.getId();
55451         }
55452         return this.getPanel(panel) ? true : false;
55453     },
55454     
55455     /**
55456      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55457      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55458      * @param {Boolean} preservePanel Overrides the config preservePanel option
55459      * @return {Roo.ContentPanel} The panel that was removed
55460      */
55461     remove : function(panel, preservePanel){
55462         panel = this.getPanel(panel);
55463         if(!panel){
55464             return null;
55465         }
55466         var e = {};
55467         this.fireEvent("beforeremove", this, panel, e);
55468         if(e.cancel === true){
55469             return null;
55470         }
55471         var panelId = panel.getId();
55472         this.panels.removeKey(panelId);
55473         return panel;
55474     },
55475     
55476     /**
55477      * Returns the panel specified or null if it's not in this region.
55478      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55479      * @return {Roo.ContentPanel}
55480      */
55481     getPanel : function(id){
55482         if(typeof id == "object"){ // must be panel obj
55483             return id;
55484         }
55485         return this.panels.get(id);
55486     },
55487     
55488     /**
55489      * Returns this regions position (north/south/east/west/center).
55490      * @return {String} 
55491      */
55492     getPosition: function(){
55493         return this.position;    
55494     }
55495 });/*
55496  * Based on:
55497  * Ext JS Library 1.1.1
55498  * Copyright(c) 2006-2007, Ext JS, LLC.
55499  *
55500  * Originally Released Under LGPL - original licence link has changed is not relivant.
55501  *
55502  * Fork - LGPL
55503  * <script type="text/javascript">
55504  */
55505  
55506 /**
55507  * @class Roo.LayoutRegion
55508  * @extends Roo.BasicLayoutRegion
55509  * This class represents a region in a layout manager.
55510  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55511  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55512  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55513  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55514  * @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})
55515  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55516  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55517  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55518  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55519  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55520  * @cfg {String}    title           The title for the region (overrides panel titles)
55521  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55522  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55523  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55524  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55525  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55526  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55527  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55528  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55529  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55530  * @cfg {Boolean}   showPin         True to show a pin button
55531  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55532  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55533  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55534  * @cfg {Number}    width           For East/West panels
55535  * @cfg {Number}    height          For North/South panels
55536  * @cfg {Boolean}   split           To show the splitter
55537  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55538  */
55539 Roo.LayoutRegion = function(mgr, config, pos){
55540     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55541     var dh = Roo.DomHelper;
55542     /** This region's container element 
55543     * @type Roo.Element */
55544     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55545     /** This region's title element 
55546     * @type Roo.Element */
55547
55548     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55549         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55550         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55551     ]}, true);
55552     this.titleEl.enableDisplayMode();
55553     /** This region's title text element 
55554     * @type HTMLElement */
55555     this.titleTextEl = this.titleEl.dom.firstChild;
55556     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55557     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55558     this.closeBtn.enableDisplayMode();
55559     this.closeBtn.on("click", this.closeClicked, this);
55560     this.closeBtn.hide();
55561
55562     this.createBody(config);
55563     this.visible = true;
55564     this.collapsed = false;
55565
55566     if(config.hideWhenEmpty){
55567         this.hide();
55568         this.on("paneladded", this.validateVisibility, this);
55569         this.on("panelremoved", this.validateVisibility, this);
55570     }
55571     this.applyConfig(config);
55572 };
55573
55574 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55575
55576     createBody : function(){
55577         /** This region's body element 
55578         * @type Roo.Element */
55579         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55580     },
55581
55582     applyConfig : function(c){
55583         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55584             var dh = Roo.DomHelper;
55585             if(c.titlebar !== false){
55586                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55587                 this.collapseBtn.on("click", this.collapse, this);
55588                 this.collapseBtn.enableDisplayMode();
55589
55590                 if(c.showPin === true || this.showPin){
55591                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55592                     this.stickBtn.enableDisplayMode();
55593                     this.stickBtn.on("click", this.expand, this);
55594                     this.stickBtn.hide();
55595                 }
55596             }
55597             /** This region's collapsed element
55598             * @type Roo.Element */
55599             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55600                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55601             ]}, true);
55602             if(c.floatable !== false){
55603                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55604                this.collapsedEl.on("click", this.collapseClick, this);
55605             }
55606
55607             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55608                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55609                    id: "message", unselectable: "on", style:{"float":"left"}});
55610                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55611              }
55612             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55613             this.expandBtn.on("click", this.expand, this);
55614         }
55615         if(this.collapseBtn){
55616             this.collapseBtn.setVisible(c.collapsible == true);
55617         }
55618         this.cmargins = c.cmargins || this.cmargins ||
55619                          (this.position == "west" || this.position == "east" ?
55620                              {top: 0, left: 2, right:2, bottom: 0} :
55621                              {top: 2, left: 0, right:0, bottom: 2});
55622         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55623         this.bottomTabs = c.tabPosition != "top";
55624         this.autoScroll = c.autoScroll || false;
55625         if(this.autoScroll){
55626             this.bodyEl.setStyle("overflow", "auto");
55627         }else{
55628             this.bodyEl.setStyle("overflow", "hidden");
55629         }
55630         //if(c.titlebar !== false){
55631             if((!c.titlebar && !c.title) || c.titlebar === false){
55632                 this.titleEl.hide();
55633             }else{
55634                 this.titleEl.show();
55635                 if(c.title){
55636                     this.titleTextEl.innerHTML = c.title;
55637                 }
55638             }
55639         //}
55640         this.duration = c.duration || .30;
55641         this.slideDuration = c.slideDuration || .45;
55642         this.config = c;
55643         if(c.collapsed){
55644             this.collapse(true);
55645         }
55646         if(c.hidden){
55647             this.hide();
55648         }
55649     },
55650     /**
55651      * Returns true if this region is currently visible.
55652      * @return {Boolean}
55653      */
55654     isVisible : function(){
55655         return this.visible;
55656     },
55657
55658     /**
55659      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55660      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55661      */
55662     setCollapsedTitle : function(title){
55663         title = title || "&#160;";
55664         if(this.collapsedTitleTextEl){
55665             this.collapsedTitleTextEl.innerHTML = title;
55666         }
55667     },
55668
55669     getBox : function(){
55670         var b;
55671         if(!this.collapsed){
55672             b = this.el.getBox(false, true);
55673         }else{
55674             b = this.collapsedEl.getBox(false, true);
55675         }
55676         return b;
55677     },
55678
55679     getMargins : function(){
55680         return this.collapsed ? this.cmargins : this.margins;
55681     },
55682
55683     highlight : function(){
55684         this.el.addClass("x-layout-panel-dragover");
55685     },
55686
55687     unhighlight : function(){
55688         this.el.removeClass("x-layout-panel-dragover");
55689     },
55690
55691     updateBox : function(box){
55692         this.box = box;
55693         if(!this.collapsed){
55694             this.el.dom.style.left = box.x + "px";
55695             this.el.dom.style.top = box.y + "px";
55696             this.updateBody(box.width, box.height);
55697         }else{
55698             this.collapsedEl.dom.style.left = box.x + "px";
55699             this.collapsedEl.dom.style.top = box.y + "px";
55700             this.collapsedEl.setSize(box.width, box.height);
55701         }
55702         if(this.tabs){
55703             this.tabs.autoSizeTabs();
55704         }
55705     },
55706
55707     updateBody : function(w, h){
55708         if(w !== null){
55709             this.el.setWidth(w);
55710             w -= this.el.getBorderWidth("rl");
55711             if(this.config.adjustments){
55712                 w += this.config.adjustments[0];
55713             }
55714         }
55715         if(h !== null){
55716             this.el.setHeight(h);
55717             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55718             h -= this.el.getBorderWidth("tb");
55719             if(this.config.adjustments){
55720                 h += this.config.adjustments[1];
55721             }
55722             this.bodyEl.setHeight(h);
55723             if(this.tabs){
55724                 h = this.tabs.syncHeight(h);
55725             }
55726         }
55727         if(this.panelSize){
55728             w = w !== null ? w : this.panelSize.width;
55729             h = h !== null ? h : this.panelSize.height;
55730         }
55731         if(this.activePanel){
55732             var el = this.activePanel.getEl();
55733             w = w !== null ? w : el.getWidth();
55734             h = h !== null ? h : el.getHeight();
55735             this.panelSize = {width: w, height: h};
55736             this.activePanel.setSize(w, h);
55737         }
55738         if(Roo.isIE && this.tabs){
55739             this.tabs.el.repaint();
55740         }
55741     },
55742
55743     /**
55744      * Returns the container element for this region.
55745      * @return {Roo.Element}
55746      */
55747     getEl : function(){
55748         return this.el;
55749     },
55750
55751     /**
55752      * Hides this region.
55753      */
55754     hide : function(){
55755         if(!this.collapsed){
55756             this.el.dom.style.left = "-2000px";
55757             this.el.hide();
55758         }else{
55759             this.collapsedEl.dom.style.left = "-2000px";
55760             this.collapsedEl.hide();
55761         }
55762         this.visible = false;
55763         this.fireEvent("visibilitychange", this, false);
55764     },
55765
55766     /**
55767      * Shows this region if it was previously hidden.
55768      */
55769     show : function(){
55770         if(!this.collapsed){
55771             this.el.show();
55772         }else{
55773             this.collapsedEl.show();
55774         }
55775         this.visible = true;
55776         this.fireEvent("visibilitychange", this, true);
55777     },
55778
55779     closeClicked : function(){
55780         if(this.activePanel){
55781             this.remove(this.activePanel);
55782         }
55783     },
55784
55785     collapseClick : function(e){
55786         if(this.isSlid){
55787            e.stopPropagation();
55788            this.slideIn();
55789         }else{
55790            e.stopPropagation();
55791            this.slideOut();
55792         }
55793     },
55794
55795     /**
55796      * Collapses this region.
55797      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55798      */
55799     collapse : function(skipAnim, skipCheck){
55800         if(this.collapsed) {
55801             return;
55802         }
55803         
55804         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55805             
55806             this.collapsed = true;
55807             if(this.split){
55808                 this.split.el.hide();
55809             }
55810             if(this.config.animate && skipAnim !== true){
55811                 this.fireEvent("invalidated", this);
55812                 this.animateCollapse();
55813             }else{
55814                 this.el.setLocation(-20000,-20000);
55815                 this.el.hide();
55816                 this.collapsedEl.show();
55817                 this.fireEvent("collapsed", this);
55818                 this.fireEvent("invalidated", this);
55819             }
55820         }
55821         
55822     },
55823
55824     animateCollapse : function(){
55825         // overridden
55826     },
55827
55828     /**
55829      * Expands this region if it was previously collapsed.
55830      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55831      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55832      */
55833     expand : function(e, skipAnim){
55834         if(e) {
55835             e.stopPropagation();
55836         }
55837         if(!this.collapsed || this.el.hasActiveFx()) {
55838             return;
55839         }
55840         if(this.isSlid){
55841             this.afterSlideIn();
55842             skipAnim = true;
55843         }
55844         this.collapsed = false;
55845         if(this.config.animate && skipAnim !== true){
55846             this.animateExpand();
55847         }else{
55848             this.el.show();
55849             if(this.split){
55850                 this.split.el.show();
55851             }
55852             this.collapsedEl.setLocation(-2000,-2000);
55853             this.collapsedEl.hide();
55854             this.fireEvent("invalidated", this);
55855             this.fireEvent("expanded", this);
55856         }
55857     },
55858
55859     animateExpand : function(){
55860         // overridden
55861     },
55862
55863     initTabs : function()
55864     {
55865         this.bodyEl.setStyle("overflow", "hidden");
55866         var ts = new Roo.TabPanel(
55867                 this.bodyEl.dom,
55868                 {
55869                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55870                     disableTooltips: this.config.disableTabTips,
55871                     toolbar : this.config.toolbar
55872                 }
55873         );
55874         if(this.config.hideTabs){
55875             ts.stripWrap.setDisplayed(false);
55876         }
55877         this.tabs = ts;
55878         ts.resizeTabs = this.config.resizeTabs === true;
55879         ts.minTabWidth = this.config.minTabWidth || 40;
55880         ts.maxTabWidth = this.config.maxTabWidth || 250;
55881         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55882         ts.monitorResize = false;
55883         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55884         ts.bodyEl.addClass('x-layout-tabs-body');
55885         this.panels.each(this.initPanelAsTab, this);
55886     },
55887
55888     initPanelAsTab : function(panel){
55889         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55890                     this.config.closeOnTab && panel.isClosable());
55891         if(panel.tabTip !== undefined){
55892             ti.setTooltip(panel.tabTip);
55893         }
55894         ti.on("activate", function(){
55895               this.setActivePanel(panel);
55896         }, this);
55897         if(this.config.closeOnTab){
55898             ti.on("beforeclose", function(t, e){
55899                 e.cancel = true;
55900                 this.remove(panel);
55901             }, this);
55902         }
55903         return ti;
55904     },
55905
55906     updatePanelTitle : function(panel, title){
55907         if(this.activePanel == panel){
55908             this.updateTitle(title);
55909         }
55910         if(this.tabs){
55911             var ti = this.tabs.getTab(panel.getEl().id);
55912             ti.setText(title);
55913             if(panel.tabTip !== undefined){
55914                 ti.setTooltip(panel.tabTip);
55915             }
55916         }
55917     },
55918
55919     updateTitle : function(title){
55920         if(this.titleTextEl && !this.config.title){
55921             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55922         }
55923     },
55924
55925     setActivePanel : function(panel){
55926         panel = this.getPanel(panel);
55927         if(this.activePanel && this.activePanel != panel){
55928             this.activePanel.setActiveState(false);
55929         }
55930         this.activePanel = panel;
55931         panel.setActiveState(true);
55932         if(this.panelSize){
55933             panel.setSize(this.panelSize.width, this.panelSize.height);
55934         }
55935         if(this.closeBtn){
55936             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55937         }
55938         this.updateTitle(panel.getTitle());
55939         if(this.tabs){
55940             this.fireEvent("invalidated", this);
55941         }
55942         this.fireEvent("panelactivated", this, panel);
55943     },
55944
55945     /**
55946      * Shows the specified panel.
55947      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55948      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55949      */
55950     showPanel : function(panel)
55951     {
55952         panel = this.getPanel(panel);
55953         if(panel){
55954             if(this.tabs){
55955                 var tab = this.tabs.getTab(panel.getEl().id);
55956                 if(tab.isHidden()){
55957                     this.tabs.unhideTab(tab.id);
55958                 }
55959                 tab.activate();
55960             }else{
55961                 this.setActivePanel(panel);
55962             }
55963         }
55964         return panel;
55965     },
55966
55967     /**
55968      * Get the active panel for this region.
55969      * @return {Roo.ContentPanel} The active panel or null
55970      */
55971     getActivePanel : function(){
55972         return this.activePanel;
55973     },
55974
55975     validateVisibility : function(){
55976         if(this.panels.getCount() < 1){
55977             this.updateTitle("&#160;");
55978             this.closeBtn.hide();
55979             this.hide();
55980         }else{
55981             if(!this.isVisible()){
55982                 this.show();
55983             }
55984         }
55985     },
55986
55987     /**
55988      * Adds the passed ContentPanel(s) to this region.
55989      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55990      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55991      */
55992     add : function(panel){
55993         if(arguments.length > 1){
55994             for(var i = 0, len = arguments.length; i < len; i++) {
55995                 this.add(arguments[i]);
55996             }
55997             return null;
55998         }
55999         if(this.hasPanel(panel)){
56000             this.showPanel(panel);
56001             return panel;
56002         }
56003         panel.setRegion(this);
56004         this.panels.add(panel);
56005         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56006             this.bodyEl.dom.appendChild(panel.getEl().dom);
56007             if(panel.background !== true){
56008                 this.setActivePanel(panel);
56009             }
56010             this.fireEvent("paneladded", this, panel);
56011             return panel;
56012         }
56013         if(!this.tabs){
56014             this.initTabs();
56015         }else{
56016             this.initPanelAsTab(panel);
56017         }
56018         if(panel.background !== true){
56019             this.tabs.activate(panel.getEl().id);
56020         }
56021         this.fireEvent("paneladded", this, panel);
56022         return panel;
56023     },
56024
56025     /**
56026      * Hides the tab for the specified panel.
56027      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56028      */
56029     hidePanel : function(panel){
56030         if(this.tabs && (panel = this.getPanel(panel))){
56031             this.tabs.hideTab(panel.getEl().id);
56032         }
56033     },
56034
56035     /**
56036      * Unhides the tab for a previously hidden panel.
56037      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56038      */
56039     unhidePanel : function(panel){
56040         if(this.tabs && (panel = this.getPanel(panel))){
56041             this.tabs.unhideTab(panel.getEl().id);
56042         }
56043     },
56044
56045     clearPanels : function(){
56046         while(this.panels.getCount() > 0){
56047              this.remove(this.panels.first());
56048         }
56049     },
56050
56051     /**
56052      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56053      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56054      * @param {Boolean} preservePanel Overrides the config preservePanel option
56055      * @return {Roo.ContentPanel} The panel that was removed
56056      */
56057     remove : function(panel, preservePanel){
56058         panel = this.getPanel(panel);
56059         if(!panel){
56060             return null;
56061         }
56062         var e = {};
56063         this.fireEvent("beforeremove", this, panel, e);
56064         if(e.cancel === true){
56065             return null;
56066         }
56067         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56068         var panelId = panel.getId();
56069         this.panels.removeKey(panelId);
56070         if(preservePanel){
56071             document.body.appendChild(panel.getEl().dom);
56072         }
56073         if(this.tabs){
56074             this.tabs.removeTab(panel.getEl().id);
56075         }else if (!preservePanel){
56076             this.bodyEl.dom.removeChild(panel.getEl().dom);
56077         }
56078         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56079             var p = this.panels.first();
56080             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56081             tempEl.appendChild(p.getEl().dom);
56082             this.bodyEl.update("");
56083             this.bodyEl.dom.appendChild(p.getEl().dom);
56084             tempEl = null;
56085             this.updateTitle(p.getTitle());
56086             this.tabs = null;
56087             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56088             this.setActivePanel(p);
56089         }
56090         panel.setRegion(null);
56091         if(this.activePanel == panel){
56092             this.activePanel = null;
56093         }
56094         if(this.config.autoDestroy !== false && preservePanel !== true){
56095             try{panel.destroy();}catch(e){}
56096         }
56097         this.fireEvent("panelremoved", this, panel);
56098         return panel;
56099     },
56100
56101     /**
56102      * Returns the TabPanel component used by this region
56103      * @return {Roo.TabPanel}
56104      */
56105     getTabs : function(){
56106         return this.tabs;
56107     },
56108
56109     createTool : function(parentEl, className){
56110         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56111             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56112         btn.addClassOnOver("x-layout-tools-button-over");
56113         return btn;
56114     }
56115 });/*
56116  * Based on:
56117  * Ext JS Library 1.1.1
56118  * Copyright(c) 2006-2007, Ext JS, LLC.
56119  *
56120  * Originally Released Under LGPL - original licence link has changed is not relivant.
56121  *
56122  * Fork - LGPL
56123  * <script type="text/javascript">
56124  */
56125  
56126
56127
56128 /**
56129  * @class Roo.SplitLayoutRegion
56130  * @extends Roo.LayoutRegion
56131  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56132  */
56133 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56134     this.cursor = cursor;
56135     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56136 };
56137
56138 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56139     splitTip : "Drag to resize.",
56140     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56141     useSplitTips : false,
56142
56143     applyConfig : function(config){
56144         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56145         if(config.split){
56146             if(!this.split){
56147                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56148                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56149                 /** The SplitBar for this region 
56150                 * @type Roo.SplitBar */
56151                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56152                 this.split.on("moved", this.onSplitMove, this);
56153                 this.split.useShim = config.useShim === true;
56154                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56155                 if(this.useSplitTips){
56156                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56157                 }
56158                 if(config.collapsible){
56159                     this.split.el.on("dblclick", this.collapse,  this);
56160                 }
56161             }
56162             if(typeof config.minSize != "undefined"){
56163                 this.split.minSize = config.minSize;
56164             }
56165             if(typeof config.maxSize != "undefined"){
56166                 this.split.maxSize = config.maxSize;
56167             }
56168             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56169                 this.hideSplitter();
56170             }
56171         }
56172     },
56173
56174     getHMaxSize : function(){
56175          var cmax = this.config.maxSize || 10000;
56176          var center = this.mgr.getRegion("center");
56177          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56178     },
56179
56180     getVMaxSize : function(){
56181          var cmax = this.config.maxSize || 10000;
56182          var center = this.mgr.getRegion("center");
56183          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56184     },
56185
56186     onSplitMove : function(split, newSize){
56187         this.fireEvent("resized", this, newSize);
56188     },
56189     
56190     /** 
56191      * Returns the {@link Roo.SplitBar} for this region.
56192      * @return {Roo.SplitBar}
56193      */
56194     getSplitBar : function(){
56195         return this.split;
56196     },
56197     
56198     hide : function(){
56199         this.hideSplitter();
56200         Roo.SplitLayoutRegion.superclass.hide.call(this);
56201     },
56202
56203     hideSplitter : function(){
56204         if(this.split){
56205             this.split.el.setLocation(-2000,-2000);
56206             this.split.el.hide();
56207         }
56208     },
56209
56210     show : function(){
56211         if(this.split){
56212             this.split.el.show();
56213         }
56214         Roo.SplitLayoutRegion.superclass.show.call(this);
56215     },
56216     
56217     beforeSlide: function(){
56218         if(Roo.isGecko){// firefox overflow auto bug workaround
56219             this.bodyEl.clip();
56220             if(this.tabs) {
56221                 this.tabs.bodyEl.clip();
56222             }
56223             if(this.activePanel){
56224                 this.activePanel.getEl().clip();
56225                 
56226                 if(this.activePanel.beforeSlide){
56227                     this.activePanel.beforeSlide();
56228                 }
56229             }
56230         }
56231     },
56232     
56233     afterSlide : function(){
56234         if(Roo.isGecko){// firefox overflow auto bug workaround
56235             this.bodyEl.unclip();
56236             if(this.tabs) {
56237                 this.tabs.bodyEl.unclip();
56238             }
56239             if(this.activePanel){
56240                 this.activePanel.getEl().unclip();
56241                 if(this.activePanel.afterSlide){
56242                     this.activePanel.afterSlide();
56243                 }
56244             }
56245         }
56246     },
56247
56248     initAutoHide : function(){
56249         if(this.autoHide !== false){
56250             if(!this.autoHideHd){
56251                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56252                 this.autoHideHd = {
56253                     "mouseout": function(e){
56254                         if(!e.within(this.el, true)){
56255                             st.delay(500);
56256                         }
56257                     },
56258                     "mouseover" : function(e){
56259                         st.cancel();
56260                     },
56261                     scope : this
56262                 };
56263             }
56264             this.el.on(this.autoHideHd);
56265         }
56266     },
56267
56268     clearAutoHide : function(){
56269         if(this.autoHide !== false){
56270             this.el.un("mouseout", this.autoHideHd.mouseout);
56271             this.el.un("mouseover", this.autoHideHd.mouseover);
56272         }
56273     },
56274
56275     clearMonitor : function(){
56276         Roo.get(document).un("click", this.slideInIf, this);
56277     },
56278
56279     // these names are backwards but not changed for compat
56280     slideOut : function(){
56281         if(this.isSlid || this.el.hasActiveFx()){
56282             return;
56283         }
56284         this.isSlid = true;
56285         if(this.collapseBtn){
56286             this.collapseBtn.hide();
56287         }
56288         this.closeBtnState = this.closeBtn.getStyle('display');
56289         this.closeBtn.hide();
56290         if(this.stickBtn){
56291             this.stickBtn.show();
56292         }
56293         this.el.show();
56294         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56295         this.beforeSlide();
56296         this.el.setStyle("z-index", 10001);
56297         this.el.slideIn(this.getSlideAnchor(), {
56298             callback: function(){
56299                 this.afterSlide();
56300                 this.initAutoHide();
56301                 Roo.get(document).on("click", this.slideInIf, this);
56302                 this.fireEvent("slideshow", this);
56303             },
56304             scope: this,
56305             block: true
56306         });
56307     },
56308
56309     afterSlideIn : function(){
56310         this.clearAutoHide();
56311         this.isSlid = false;
56312         this.clearMonitor();
56313         this.el.setStyle("z-index", "");
56314         if(this.collapseBtn){
56315             this.collapseBtn.show();
56316         }
56317         this.closeBtn.setStyle('display', this.closeBtnState);
56318         if(this.stickBtn){
56319             this.stickBtn.hide();
56320         }
56321         this.fireEvent("slidehide", this);
56322     },
56323
56324     slideIn : function(cb){
56325         if(!this.isSlid || this.el.hasActiveFx()){
56326             Roo.callback(cb);
56327             return;
56328         }
56329         this.isSlid = false;
56330         this.beforeSlide();
56331         this.el.slideOut(this.getSlideAnchor(), {
56332             callback: function(){
56333                 this.el.setLeftTop(-10000, -10000);
56334                 this.afterSlide();
56335                 this.afterSlideIn();
56336                 Roo.callback(cb);
56337             },
56338             scope: this,
56339             block: true
56340         });
56341     },
56342     
56343     slideInIf : function(e){
56344         if(!e.within(this.el)){
56345             this.slideIn();
56346         }
56347     },
56348
56349     animateCollapse : function(){
56350         this.beforeSlide();
56351         this.el.setStyle("z-index", 20000);
56352         var anchor = this.getSlideAnchor();
56353         this.el.slideOut(anchor, {
56354             callback : function(){
56355                 this.el.setStyle("z-index", "");
56356                 this.collapsedEl.slideIn(anchor, {duration:.3});
56357                 this.afterSlide();
56358                 this.el.setLocation(-10000,-10000);
56359                 this.el.hide();
56360                 this.fireEvent("collapsed", this);
56361             },
56362             scope: this,
56363             block: true
56364         });
56365     },
56366
56367     animateExpand : function(){
56368         this.beforeSlide();
56369         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56370         this.el.setStyle("z-index", 20000);
56371         this.collapsedEl.hide({
56372             duration:.1
56373         });
56374         this.el.slideIn(this.getSlideAnchor(), {
56375             callback : function(){
56376                 this.el.setStyle("z-index", "");
56377                 this.afterSlide();
56378                 if(this.split){
56379                     this.split.el.show();
56380                 }
56381                 this.fireEvent("invalidated", this);
56382                 this.fireEvent("expanded", this);
56383             },
56384             scope: this,
56385             block: true
56386         });
56387     },
56388
56389     anchors : {
56390         "west" : "left",
56391         "east" : "right",
56392         "north" : "top",
56393         "south" : "bottom"
56394     },
56395
56396     sanchors : {
56397         "west" : "l",
56398         "east" : "r",
56399         "north" : "t",
56400         "south" : "b"
56401     },
56402
56403     canchors : {
56404         "west" : "tl-tr",
56405         "east" : "tr-tl",
56406         "north" : "tl-bl",
56407         "south" : "bl-tl"
56408     },
56409
56410     getAnchor : function(){
56411         return this.anchors[this.position];
56412     },
56413
56414     getCollapseAnchor : function(){
56415         return this.canchors[this.position];
56416     },
56417
56418     getSlideAnchor : function(){
56419         return this.sanchors[this.position];
56420     },
56421
56422     getAlignAdj : function(){
56423         var cm = this.cmargins;
56424         switch(this.position){
56425             case "west":
56426                 return [0, 0];
56427             break;
56428             case "east":
56429                 return [0, 0];
56430             break;
56431             case "north":
56432                 return [0, 0];
56433             break;
56434             case "south":
56435                 return [0, 0];
56436             break;
56437         }
56438     },
56439
56440     getExpandAdj : function(){
56441         var c = this.collapsedEl, cm = this.cmargins;
56442         switch(this.position){
56443             case "west":
56444                 return [-(cm.right+c.getWidth()+cm.left), 0];
56445             break;
56446             case "east":
56447                 return [cm.right+c.getWidth()+cm.left, 0];
56448             break;
56449             case "north":
56450                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56451             break;
56452             case "south":
56453                 return [0, cm.top+cm.bottom+c.getHeight()];
56454             break;
56455         }
56456     }
56457 });/*
56458  * Based on:
56459  * Ext JS Library 1.1.1
56460  * Copyright(c) 2006-2007, Ext JS, LLC.
56461  *
56462  * Originally Released Under LGPL - original licence link has changed is not relivant.
56463  *
56464  * Fork - LGPL
56465  * <script type="text/javascript">
56466  */
56467 /*
56468  * These classes are private internal classes
56469  */
56470 Roo.CenterLayoutRegion = function(mgr, config){
56471     Roo.LayoutRegion.call(this, mgr, config, "center");
56472     this.visible = true;
56473     this.minWidth = config.minWidth || 20;
56474     this.minHeight = config.minHeight || 20;
56475 };
56476
56477 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56478     hide : function(){
56479         // center panel can't be hidden
56480     },
56481     
56482     show : function(){
56483         // center panel can't be hidden
56484     },
56485     
56486     getMinWidth: function(){
56487         return this.minWidth;
56488     },
56489     
56490     getMinHeight: function(){
56491         return this.minHeight;
56492     }
56493 });
56494
56495
56496 Roo.NorthLayoutRegion = function(mgr, config){
56497     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56498     if(this.split){
56499         this.split.placement = Roo.SplitBar.TOP;
56500         this.split.orientation = Roo.SplitBar.VERTICAL;
56501         this.split.el.addClass("x-layout-split-v");
56502     }
56503     var size = config.initialSize || config.height;
56504     if(typeof size != "undefined"){
56505         this.el.setHeight(size);
56506     }
56507 };
56508 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56509     orientation: Roo.SplitBar.VERTICAL,
56510     getBox : function(){
56511         if(this.collapsed){
56512             return this.collapsedEl.getBox();
56513         }
56514         var box = this.el.getBox();
56515         if(this.split){
56516             box.height += this.split.el.getHeight();
56517         }
56518         return box;
56519     },
56520     
56521     updateBox : function(box){
56522         if(this.split && !this.collapsed){
56523             box.height -= this.split.el.getHeight();
56524             this.split.el.setLeft(box.x);
56525             this.split.el.setTop(box.y+box.height);
56526             this.split.el.setWidth(box.width);
56527         }
56528         if(this.collapsed){
56529             this.updateBody(box.width, null);
56530         }
56531         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56532     }
56533 });
56534
56535 Roo.SouthLayoutRegion = function(mgr, config){
56536     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56537     if(this.split){
56538         this.split.placement = Roo.SplitBar.BOTTOM;
56539         this.split.orientation = Roo.SplitBar.VERTICAL;
56540         this.split.el.addClass("x-layout-split-v");
56541     }
56542     var size = config.initialSize || config.height;
56543     if(typeof size != "undefined"){
56544         this.el.setHeight(size);
56545     }
56546 };
56547 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56548     orientation: Roo.SplitBar.VERTICAL,
56549     getBox : function(){
56550         if(this.collapsed){
56551             return this.collapsedEl.getBox();
56552         }
56553         var box = this.el.getBox();
56554         if(this.split){
56555             var sh = this.split.el.getHeight();
56556             box.height += sh;
56557             box.y -= sh;
56558         }
56559         return box;
56560     },
56561     
56562     updateBox : function(box){
56563         if(this.split && !this.collapsed){
56564             var sh = this.split.el.getHeight();
56565             box.height -= sh;
56566             box.y += sh;
56567             this.split.el.setLeft(box.x);
56568             this.split.el.setTop(box.y-sh);
56569             this.split.el.setWidth(box.width);
56570         }
56571         if(this.collapsed){
56572             this.updateBody(box.width, null);
56573         }
56574         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56575     }
56576 });
56577
56578 Roo.EastLayoutRegion = function(mgr, config){
56579     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56580     if(this.split){
56581         this.split.placement = Roo.SplitBar.RIGHT;
56582         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56583         this.split.el.addClass("x-layout-split-h");
56584     }
56585     var size = config.initialSize || config.width;
56586     if(typeof size != "undefined"){
56587         this.el.setWidth(size);
56588     }
56589 };
56590 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56591     orientation: Roo.SplitBar.HORIZONTAL,
56592     getBox : function(){
56593         if(this.collapsed){
56594             return this.collapsedEl.getBox();
56595         }
56596         var box = this.el.getBox();
56597         if(this.split){
56598             var sw = this.split.el.getWidth();
56599             box.width += sw;
56600             box.x -= sw;
56601         }
56602         return box;
56603     },
56604
56605     updateBox : function(box){
56606         if(this.split && !this.collapsed){
56607             var sw = this.split.el.getWidth();
56608             box.width -= sw;
56609             this.split.el.setLeft(box.x);
56610             this.split.el.setTop(box.y);
56611             this.split.el.setHeight(box.height);
56612             box.x += sw;
56613         }
56614         if(this.collapsed){
56615             this.updateBody(null, box.height);
56616         }
56617         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56618     }
56619 });
56620
56621 Roo.WestLayoutRegion = function(mgr, config){
56622     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56623     if(this.split){
56624         this.split.placement = Roo.SplitBar.LEFT;
56625         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56626         this.split.el.addClass("x-layout-split-h");
56627     }
56628     var size = config.initialSize || config.width;
56629     if(typeof size != "undefined"){
56630         this.el.setWidth(size);
56631     }
56632 };
56633 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56634     orientation: Roo.SplitBar.HORIZONTAL,
56635     getBox : function(){
56636         if(this.collapsed){
56637             return this.collapsedEl.getBox();
56638         }
56639         var box = this.el.getBox();
56640         if(this.split){
56641             box.width += this.split.el.getWidth();
56642         }
56643         return box;
56644     },
56645     
56646     updateBox : function(box){
56647         if(this.split && !this.collapsed){
56648             var sw = this.split.el.getWidth();
56649             box.width -= sw;
56650             this.split.el.setLeft(box.x+box.width);
56651             this.split.el.setTop(box.y);
56652             this.split.el.setHeight(box.height);
56653         }
56654         if(this.collapsed){
56655             this.updateBody(null, box.height);
56656         }
56657         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56658     }
56659 });
56660 /*
56661  * Based on:
56662  * Ext JS Library 1.1.1
56663  * Copyright(c) 2006-2007, Ext JS, LLC.
56664  *
56665  * Originally Released Under LGPL - original licence link has changed is not relivant.
56666  *
56667  * Fork - LGPL
56668  * <script type="text/javascript">
56669  */
56670  
56671  
56672 /*
56673  * Private internal class for reading and applying state
56674  */
56675 Roo.LayoutStateManager = function(layout){
56676      // default empty state
56677      this.state = {
56678         north: {},
56679         south: {},
56680         east: {},
56681         west: {}       
56682     };
56683 };
56684
56685 Roo.LayoutStateManager.prototype = {
56686     init : function(layout, provider){
56687         this.provider = provider;
56688         var state = provider.get(layout.id+"-layout-state");
56689         if(state){
56690             var wasUpdating = layout.isUpdating();
56691             if(!wasUpdating){
56692                 layout.beginUpdate();
56693             }
56694             for(var key in state){
56695                 if(typeof state[key] != "function"){
56696                     var rstate = state[key];
56697                     var r = layout.getRegion(key);
56698                     if(r && rstate){
56699                         if(rstate.size){
56700                             r.resizeTo(rstate.size);
56701                         }
56702                         if(rstate.collapsed == true){
56703                             r.collapse(true);
56704                         }else{
56705                             r.expand(null, true);
56706                         }
56707                     }
56708                 }
56709             }
56710             if(!wasUpdating){
56711                 layout.endUpdate();
56712             }
56713             this.state = state; 
56714         }
56715         this.layout = layout;
56716         layout.on("regionresized", this.onRegionResized, this);
56717         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56718         layout.on("regionexpanded", this.onRegionExpanded, this);
56719     },
56720     
56721     storeState : function(){
56722         this.provider.set(this.layout.id+"-layout-state", this.state);
56723     },
56724     
56725     onRegionResized : function(region, newSize){
56726         this.state[region.getPosition()].size = newSize;
56727         this.storeState();
56728     },
56729     
56730     onRegionCollapsed : function(region){
56731         this.state[region.getPosition()].collapsed = true;
56732         this.storeState();
56733     },
56734     
56735     onRegionExpanded : function(region){
56736         this.state[region.getPosition()].collapsed = false;
56737         this.storeState();
56738     }
56739 };/*
56740  * Based on:
56741  * Ext JS Library 1.1.1
56742  * Copyright(c) 2006-2007, Ext JS, LLC.
56743  *
56744  * Originally Released Under LGPL - original licence link has changed is not relivant.
56745  *
56746  * Fork - LGPL
56747  * <script type="text/javascript">
56748  */
56749 /**
56750  * @class Roo.ContentPanel
56751  * @extends Roo.util.Observable
56752  * @children Roo.form.Form Roo.JsonView Roo.View
56753  * @parent Roo.BorderLayout Roo.LayoutDialog builder
56754  * A basic ContentPanel element.
56755  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56756  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56757  * @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
56758  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56759  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56760  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56761  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56762  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56763  * @cfg {String} title          The title for this panel
56764  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56765  * @cfg {String} url            Calls {@link #setUrl} with this value
56766  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
56767  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56768  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56769  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56770  * @cfg {String}    style  Extra style to add to the content panel
56771  * @cfg {Roo.menu.Menu} menu  popup menu
56772
56773  * @constructor
56774  * Create a new ContentPanel.
56775  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56776  * @param {String/Object} config A string to set only the title or a config object
56777  * @param {String} content (optional) Set the HTML content for this panel
56778  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56779  */
56780 Roo.ContentPanel = function(el, config, content){
56781     
56782      
56783     /*
56784     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56785         config = el;
56786         el = Roo.id();
56787     }
56788     if (config && config.parentLayout) { 
56789         el = config.parentLayout.el.createChild(); 
56790     }
56791     */
56792     if(el.autoCreate){ // xtype is available if this is called from factory
56793         config = el;
56794         el = Roo.id();
56795     }
56796     this.el = Roo.get(el);
56797     if(!this.el && config && config.autoCreate){
56798         if(typeof config.autoCreate == "object"){
56799             if(!config.autoCreate.id){
56800                 config.autoCreate.id = config.id||el;
56801             }
56802             this.el = Roo.DomHelper.append(document.body,
56803                         config.autoCreate, true);
56804         }else{
56805             this.el = Roo.DomHelper.append(document.body,
56806                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56807         }
56808     }
56809     
56810     
56811     this.closable = false;
56812     this.loaded = false;
56813     this.active = false;
56814     if(typeof config == "string"){
56815         this.title = config;
56816     }else{
56817         Roo.apply(this, config);
56818     }
56819     
56820     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56821         this.wrapEl = this.el.wrap();
56822         this.toolbar.container = this.el.insertSibling(false, 'before');
56823         this.toolbar = new Roo.Toolbar(this.toolbar);
56824     }
56825     
56826     // xtype created footer. - not sure if will work as we normally have to render first..
56827     if (this.footer && !this.footer.el && this.footer.xtype) {
56828         if (!this.wrapEl) {
56829             this.wrapEl = this.el.wrap();
56830         }
56831     
56832         this.footer.container = this.wrapEl.createChild();
56833          
56834         this.footer = Roo.factory(this.footer, Roo);
56835         
56836     }
56837     
56838     if(this.resizeEl){
56839         this.resizeEl = Roo.get(this.resizeEl, true);
56840     }else{
56841         this.resizeEl = this.el;
56842     }
56843     // handle view.xtype
56844     
56845  
56846     
56847     
56848     this.addEvents({
56849         /**
56850          * @event activate
56851          * Fires when this panel is activated. 
56852          * @param {Roo.ContentPanel} this
56853          */
56854         "activate" : true,
56855         /**
56856          * @event deactivate
56857          * Fires when this panel is activated. 
56858          * @param {Roo.ContentPanel} this
56859          */
56860         "deactivate" : true,
56861
56862         /**
56863          * @event resize
56864          * Fires when this panel is resized if fitToFrame is true.
56865          * @param {Roo.ContentPanel} this
56866          * @param {Number} width The width after any component adjustments
56867          * @param {Number} height The height after any component adjustments
56868          */
56869         "resize" : true,
56870         
56871          /**
56872          * @event render
56873          * Fires when this tab is created
56874          * @param {Roo.ContentPanel} this
56875          */
56876         "render" : true
56877          
56878         
56879     });
56880     
56881
56882     
56883     
56884     if(this.autoScroll){
56885         this.resizeEl.setStyle("overflow", "auto");
56886     } else {
56887         // fix randome scrolling
56888         this.el.on('scroll', function() {
56889             Roo.log('fix random scolling');
56890             this.scrollTo('top',0); 
56891         });
56892     }
56893     content = content || this.content;
56894     if(content){
56895         this.setContent(content);
56896     }
56897     if(config && config.url){
56898         this.setUrl(this.url, this.params, this.loadOnce);
56899     }
56900     
56901     
56902     
56903     Roo.ContentPanel.superclass.constructor.call(this);
56904     
56905     if (this.view && typeof(this.view.xtype) != 'undefined') {
56906         this.view.el = this.el.appendChild(document.createElement("div"));
56907         this.view = Roo.factory(this.view); 
56908         this.view.render  &&  this.view.render(false, '');  
56909     }
56910     
56911     
56912     this.fireEvent('render', this);
56913 };
56914
56915 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56916     tabTip:'',
56917     setRegion : function(region){
56918         this.region = region;
56919         if(region){
56920            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56921         }else{
56922            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56923         } 
56924     },
56925     
56926     /**
56927      * Returns the toolbar for this Panel if one was configured. 
56928      * @return {Roo.Toolbar} 
56929      */
56930     getToolbar : function(){
56931         return this.toolbar;
56932     },
56933     
56934     setActiveState : function(active){
56935         this.active = active;
56936         if(!active){
56937             this.fireEvent("deactivate", this);
56938         }else{
56939             this.fireEvent("activate", this);
56940         }
56941     },
56942     /**
56943      * Updates this panel's element
56944      * @param {String} content The new content
56945      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56946     */
56947     setContent : function(content, loadScripts){
56948         this.el.update(content, loadScripts);
56949     },
56950
56951     ignoreResize : function(w, h){
56952         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56953             return true;
56954         }else{
56955             this.lastSize = {width: w, height: h};
56956             return false;
56957         }
56958     },
56959     /**
56960      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56961      * @return {Roo.UpdateManager} The UpdateManager
56962      */
56963     getUpdateManager : function(){
56964         return this.el.getUpdateManager();
56965     },
56966      /**
56967      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56968      * @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:
56969 <pre><code>
56970 panel.load({
56971     url: "your-url.php",
56972     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56973     callback: yourFunction,
56974     scope: yourObject, //(optional scope)
56975     discardUrl: false,
56976     nocache: false,
56977     text: "Loading...",
56978     timeout: 30,
56979     scripts: false
56980 });
56981 </code></pre>
56982      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56983      * 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.
56984      * @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}
56985      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56986      * @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.
56987      * @return {Roo.ContentPanel} this
56988      */
56989     load : function(){
56990         var um = this.el.getUpdateManager();
56991         um.update.apply(um, arguments);
56992         return this;
56993     },
56994
56995
56996     /**
56997      * 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.
56998      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56999      * @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)
57000      * @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)
57001      * @return {Roo.UpdateManager} The UpdateManager
57002      */
57003     setUrl : function(url, params, loadOnce){
57004         if(this.refreshDelegate){
57005             this.removeListener("activate", this.refreshDelegate);
57006         }
57007         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57008         this.on("activate", this.refreshDelegate);
57009         return this.el.getUpdateManager();
57010     },
57011     
57012     _handleRefresh : function(url, params, loadOnce){
57013         if(!loadOnce || !this.loaded){
57014             var updater = this.el.getUpdateManager();
57015             updater.update(url, params, this._setLoaded.createDelegate(this));
57016         }
57017     },
57018     
57019     _setLoaded : function(){
57020         this.loaded = true;
57021     }, 
57022     
57023     /**
57024      * Returns this panel's id
57025      * @return {String} 
57026      */
57027     getId : function(){
57028         return this.el.id;
57029     },
57030     
57031     /** 
57032      * Returns this panel's element - used by regiosn to add.
57033      * @return {Roo.Element} 
57034      */
57035     getEl : function(){
57036         return this.wrapEl || this.el;
57037     },
57038     
57039     adjustForComponents : function(width, height)
57040     {
57041         //Roo.log('adjustForComponents ');
57042         if(this.resizeEl != this.el){
57043             width -= this.el.getFrameWidth('lr');
57044             height -= this.el.getFrameWidth('tb');
57045         }
57046         if(this.toolbar){
57047             var te = this.toolbar.getEl();
57048             height -= te.getHeight();
57049             te.setWidth(width);
57050         }
57051         if(this.footer){
57052             var te = this.footer.getEl();
57053             //Roo.log("footer:" + te.getHeight());
57054             
57055             height -= te.getHeight();
57056             te.setWidth(width);
57057         }
57058         
57059         
57060         if(this.adjustments){
57061             width += this.adjustments[0];
57062             height += this.adjustments[1];
57063         }
57064         return {"width": width, "height": height};
57065     },
57066     
57067     setSize : function(width, height){
57068         if(this.fitToFrame && !this.ignoreResize(width, height)){
57069             if(this.fitContainer && this.resizeEl != this.el){
57070                 this.el.setSize(width, height);
57071             }
57072             var size = this.adjustForComponents(width, height);
57073             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57074             this.fireEvent('resize', this, size.width, size.height);
57075         }
57076     },
57077     
57078     /**
57079      * Returns this panel's title
57080      * @return {String} 
57081      */
57082     getTitle : function(){
57083         return this.title;
57084     },
57085     
57086     /**
57087      * Set this panel's title
57088      * @param {String} title
57089      */
57090     setTitle : function(title){
57091         this.title = title;
57092         if(this.region){
57093             this.region.updatePanelTitle(this, title);
57094         }
57095     },
57096     
57097     /**
57098      * Returns true is this panel was configured to be closable
57099      * @return {Boolean} 
57100      */
57101     isClosable : function(){
57102         return this.closable;
57103     },
57104     
57105     beforeSlide : function(){
57106         this.el.clip();
57107         this.resizeEl.clip();
57108     },
57109     
57110     afterSlide : function(){
57111         this.el.unclip();
57112         this.resizeEl.unclip();
57113     },
57114     
57115     /**
57116      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57117      *   Will fail silently if the {@link #setUrl} method has not been called.
57118      *   This does not activate the panel, just updates its content.
57119      */
57120     refresh : function(){
57121         if(this.refreshDelegate){
57122            this.loaded = false;
57123            this.refreshDelegate();
57124         }
57125     },
57126     
57127     /**
57128      * Destroys this panel
57129      */
57130     destroy : function(){
57131         this.el.removeAllListeners();
57132         var tempEl = document.createElement("span");
57133         tempEl.appendChild(this.el.dom);
57134         tempEl.innerHTML = "";
57135         this.el.remove();
57136         this.el = null;
57137     },
57138     
57139     /**
57140      * form - if the content panel contains a form - this is a reference to it.
57141      * @type {Roo.form.Form}
57142      */
57143     form : false,
57144     /**
57145      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57146      *    This contains a reference to it.
57147      * @type {Roo.View}
57148      */
57149     view : false,
57150     
57151       /**
57152      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57153      * <pre><code>
57154
57155 layout.addxtype({
57156        xtype : 'Form',
57157        items: [ .... ]
57158    }
57159 );
57160
57161 </code></pre>
57162      * @param {Object} cfg Xtype definition of item to add.
57163      */
57164     
57165     addxtype : function(cfg) {
57166         // add form..
57167         if (cfg.xtype.match(/^Form$/)) {
57168             
57169             var el;
57170             //if (this.footer) {
57171             //    el = this.footer.container.insertSibling(false, 'before');
57172             //} else {
57173                 el = this.el.createChild();
57174             //}
57175
57176             this.form = new  Roo.form.Form(cfg);
57177             
57178             
57179             if ( this.form.allItems.length) {
57180                 this.form.render(el.dom);
57181             }
57182             return this.form;
57183         }
57184         // should only have one of theses..
57185         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57186             // views.. should not be just added - used named prop 'view''
57187             
57188             cfg.el = this.el.appendChild(document.createElement("div"));
57189             // factory?
57190             
57191             var ret = new Roo.factory(cfg);
57192              
57193              ret.render && ret.render(false, ''); // render blank..
57194             this.view = ret;
57195             return ret;
57196         }
57197         return false;
57198     }
57199 });
57200
57201 /**
57202  * @class Roo.GridPanel
57203  * @extends Roo.ContentPanel
57204  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57205  * @constructor
57206  * Create a new GridPanel.
57207  * @cfg {Roo.grid.Grid} grid The grid for this panel
57208  */
57209 Roo.GridPanel = function(grid, config){
57210     
57211     // universal ctor...
57212     if (typeof(grid.grid) != 'undefined') {
57213         config = grid;
57214         grid = config.grid;
57215     }
57216     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57217         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57218         
57219     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57220     
57221     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57222     
57223     if(this.toolbar){
57224         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57225     }
57226     // xtype created footer. - not sure if will work as we normally have to render first..
57227     if (this.footer && !this.footer.el && this.footer.xtype) {
57228         
57229         this.footer.container = this.grid.getView().getFooterPanel(true);
57230         this.footer.dataSource = this.grid.dataSource;
57231         this.footer = Roo.factory(this.footer, Roo);
57232         
57233     }
57234     
57235     grid.monitorWindowResize = false; // turn off autosizing
57236     grid.autoHeight = false;
57237     grid.autoWidth = false;
57238     this.grid = grid;
57239     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57240 };
57241
57242 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57243     getId : function(){
57244         return this.grid.id;
57245     },
57246     
57247     /**
57248      * Returns the grid for this panel
57249      * @return {Roo.grid.Grid} 
57250      */
57251     getGrid : function(){
57252         return this.grid;    
57253     },
57254     
57255     setSize : function(width, height){
57256         if(!this.ignoreResize(width, height)){
57257             var grid = this.grid;
57258             var size = this.adjustForComponents(width, height);
57259             grid.getGridEl().setSize(size.width, size.height);
57260             grid.autoSize();
57261         }
57262     },
57263     
57264     beforeSlide : function(){
57265         this.grid.getView().scroller.clip();
57266     },
57267     
57268     afterSlide : function(){
57269         this.grid.getView().scroller.unclip();
57270     },
57271     
57272     destroy : function(){
57273         this.grid.destroy();
57274         delete this.grid;
57275         Roo.GridPanel.superclass.destroy.call(this); 
57276     }
57277 });
57278
57279
57280 /**
57281  * @class Roo.NestedLayoutPanel
57282  * @extends Roo.ContentPanel
57283  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57284  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57285  *
57286  * 
57287  * @constructor
57288  * Create a new NestedLayoutPanel.
57289  * 
57290  * 
57291  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57292  * @param {String/Object} config A string to set only the title or a config object
57293  */
57294 Roo.NestedLayoutPanel = function(layout, config)
57295 {
57296     // construct with only one argument..
57297     /* FIXME - implement nicer consturctors
57298     if (layout.layout) {
57299         config = layout;
57300         layout = config.layout;
57301         delete config.layout;
57302     }
57303     if (layout.xtype && !layout.getEl) {
57304         // then layout needs constructing..
57305         layout = Roo.factory(layout, Roo);
57306     }
57307     */
57308     
57309     
57310     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57311     
57312     layout.monitorWindowResize = false; // turn off autosizing
57313     this.layout = layout;
57314     this.layout.getEl().addClass("x-layout-nested-layout");
57315     
57316     
57317     
57318     
57319 };
57320
57321 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57322
57323     setSize : function(width, height){
57324         if(!this.ignoreResize(width, height)){
57325             var size = this.adjustForComponents(width, height);
57326             var el = this.layout.getEl();
57327             el.setSize(size.width, size.height);
57328             var touch = el.dom.offsetWidth;
57329             this.layout.layout();
57330             // ie requires a double layout on the first pass
57331             if(Roo.isIE && !this.initialized){
57332                 this.initialized = true;
57333                 this.layout.layout();
57334             }
57335         }
57336     },
57337     
57338     // activate all subpanels if not currently active..
57339     
57340     setActiveState : function(active){
57341         this.active = active;
57342         if(!active){
57343             this.fireEvent("deactivate", this);
57344             return;
57345         }
57346         
57347         this.fireEvent("activate", this);
57348         // not sure if this should happen before or after..
57349         if (!this.layout) {
57350             return; // should not happen..
57351         }
57352         var reg = false;
57353         for (var r in this.layout.regions) {
57354             reg = this.layout.getRegion(r);
57355             if (reg.getActivePanel()) {
57356                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57357                 reg.setActivePanel(reg.getActivePanel());
57358                 continue;
57359             }
57360             if (!reg.panels.length) {
57361                 continue;
57362             }
57363             reg.showPanel(reg.getPanel(0));
57364         }
57365         
57366         
57367         
57368         
57369     },
57370     
57371     /**
57372      * Returns the nested BorderLayout for this panel
57373      * @return {Roo.BorderLayout} 
57374      */
57375     getLayout : function(){
57376         return this.layout;
57377     },
57378     
57379      /**
57380      * Adds a xtype elements to the layout of the nested panel
57381      * <pre><code>
57382
57383 panel.addxtype({
57384        xtype : 'ContentPanel',
57385        region: 'west',
57386        items: [ .... ]
57387    }
57388 );
57389
57390 panel.addxtype({
57391         xtype : 'NestedLayoutPanel',
57392         region: 'west',
57393         layout: {
57394            center: { },
57395            west: { }   
57396         },
57397         items : [ ... list of content panels or nested layout panels.. ]
57398    }
57399 );
57400 </code></pre>
57401      * @param {Object} cfg Xtype definition of item to add.
57402      */
57403     addxtype : function(cfg) {
57404         return this.layout.addxtype(cfg);
57405     
57406     }
57407 });
57408
57409 Roo.ScrollPanel = function(el, config, content){
57410     config = config || {};
57411     config.fitToFrame = true;
57412     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57413     
57414     this.el.dom.style.overflow = "hidden";
57415     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57416     this.el.removeClass("x-layout-inactive-content");
57417     this.el.on("mousewheel", this.onWheel, this);
57418
57419     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57420     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57421     up.unselectable(); down.unselectable();
57422     up.on("click", this.scrollUp, this);
57423     down.on("click", this.scrollDown, this);
57424     up.addClassOnOver("x-scroller-btn-over");
57425     down.addClassOnOver("x-scroller-btn-over");
57426     up.addClassOnClick("x-scroller-btn-click");
57427     down.addClassOnClick("x-scroller-btn-click");
57428     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57429
57430     this.resizeEl = this.el;
57431     this.el = wrap; this.up = up; this.down = down;
57432 };
57433
57434 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57435     increment : 100,
57436     wheelIncrement : 5,
57437     scrollUp : function(){
57438         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57439     },
57440
57441     scrollDown : function(){
57442         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57443     },
57444
57445     afterScroll : function(){
57446         var el = this.resizeEl;
57447         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57448         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57449         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57450     },
57451
57452     setSize : function(){
57453         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57454         this.afterScroll();
57455     },
57456
57457     onWheel : function(e){
57458         var d = e.getWheelDelta();
57459         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57460         this.afterScroll();
57461         e.stopEvent();
57462     },
57463
57464     setContent : function(content, loadScripts){
57465         this.resizeEl.update(content, loadScripts);
57466     }
57467
57468 });
57469
57470
57471
57472 /**
57473  * @class Roo.TreePanel
57474  * @extends Roo.ContentPanel
57475  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57476  * Treepanel component
57477  * 
57478  * @constructor
57479  * Create a new TreePanel. - defaults to fit/scoll contents.
57480  * @param {String/Object} config A string to set only the panel's title, or a config object
57481  */
57482 Roo.TreePanel = function(config){
57483     var el = config.el;
57484     var tree = config.tree;
57485     delete config.tree; 
57486     delete config.el; // hopefull!
57487     
57488     // wrapper for IE7 strict & safari scroll issue
57489     
57490     var treeEl = el.createChild();
57491     config.resizeEl = treeEl;
57492     
57493     
57494     
57495     Roo.TreePanel.superclass.constructor.call(this, el, config);
57496  
57497  
57498     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57499     //console.log(tree);
57500     this.on('activate', function()
57501     {
57502         if (this.tree.rendered) {
57503             return;
57504         }
57505         //console.log('render tree');
57506         this.tree.render();
57507     });
57508     // this should not be needed.. - it's actually the 'el' that resizes?
57509     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57510     
57511     //this.on('resize',  function (cp, w, h) {
57512     //        this.tree.innerCt.setWidth(w);
57513     //        this.tree.innerCt.setHeight(h);
57514     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57515     //});
57516
57517         
57518     
57519 };
57520
57521 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57522     fitToFrame : true,
57523     autoScroll : true,
57524     /*
57525      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57526      */
57527     tree : false
57528
57529 });
57530
57531
57532
57533
57534
57535
57536
57537
57538
57539
57540
57541 /*
57542  * Based on:
57543  * Ext JS Library 1.1.1
57544  * Copyright(c) 2006-2007, Ext JS, LLC.
57545  *
57546  * Originally Released Under LGPL - original licence link has changed is not relivant.
57547  *
57548  * Fork - LGPL
57549  * <script type="text/javascript">
57550  */
57551  
57552
57553 /**
57554  * @class Roo.ReaderLayout
57555  * @extends Roo.BorderLayout
57556  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57557  * center region containing two nested regions (a top one for a list view and one for item preview below),
57558  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57559  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57560  * expedites the setup of the overall layout and regions for this common application style.
57561  * Example:
57562  <pre><code>
57563 var reader = new Roo.ReaderLayout();
57564 var CP = Roo.ContentPanel;  // shortcut for adding
57565
57566 reader.beginUpdate();
57567 reader.add("north", new CP("north", "North"));
57568 reader.add("west", new CP("west", {title: "West"}));
57569 reader.add("east", new CP("east", {title: "East"}));
57570
57571 reader.regions.listView.add(new CP("listView", "List"));
57572 reader.regions.preview.add(new CP("preview", "Preview"));
57573 reader.endUpdate();
57574 </code></pre>
57575 * @constructor
57576 * Create a new ReaderLayout
57577 * @param {Object} config Configuration options
57578 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57579 * document.body if omitted)
57580 */
57581 Roo.ReaderLayout = function(config, renderTo){
57582     var c = config || {size:{}};
57583     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57584         north: c.north !== false ? Roo.apply({
57585             split:false,
57586             initialSize: 32,
57587             titlebar: false
57588         }, c.north) : false,
57589         west: c.west !== false ? Roo.apply({
57590             split:true,
57591             initialSize: 200,
57592             minSize: 175,
57593             maxSize: 400,
57594             titlebar: true,
57595             collapsible: true,
57596             animate: true,
57597             margins:{left:5,right:0,bottom:5,top:5},
57598             cmargins:{left:5,right:5,bottom:5,top:5}
57599         }, c.west) : false,
57600         east: c.east !== false ? Roo.apply({
57601             split:true,
57602             initialSize: 200,
57603             minSize: 175,
57604             maxSize: 400,
57605             titlebar: true,
57606             collapsible: true,
57607             animate: true,
57608             margins:{left:0,right:5,bottom:5,top:5},
57609             cmargins:{left:5,right:5,bottom:5,top:5}
57610         }, c.east) : false,
57611         center: Roo.apply({
57612             tabPosition: 'top',
57613             autoScroll:false,
57614             closeOnTab: true,
57615             titlebar:false,
57616             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57617         }, c.center)
57618     });
57619
57620     this.el.addClass('x-reader');
57621
57622     this.beginUpdate();
57623
57624     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57625         south: c.preview !== false ? Roo.apply({
57626             split:true,
57627             initialSize: 200,
57628             minSize: 100,
57629             autoScroll:true,
57630             collapsible:true,
57631             titlebar: true,
57632             cmargins:{top:5,left:0, right:0, bottom:0}
57633         }, c.preview) : false,
57634         center: Roo.apply({
57635             autoScroll:false,
57636             titlebar:false,
57637             minHeight:200
57638         }, c.listView)
57639     });
57640     this.add('center', new Roo.NestedLayoutPanel(inner,
57641             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57642
57643     this.endUpdate();
57644
57645     this.regions.preview = inner.getRegion('south');
57646     this.regions.listView = inner.getRegion('center');
57647 };
57648
57649 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57650  * Based on:
57651  * Ext JS Library 1.1.1
57652  * Copyright(c) 2006-2007, Ext JS, LLC.
57653  *
57654  * Originally Released Under LGPL - original licence link has changed is not relivant.
57655  *
57656  * Fork - LGPL
57657  * <script type="text/javascript">
57658  */
57659  
57660 /**
57661  * @class Roo.grid.Grid
57662  * @extends Roo.util.Observable
57663  * This class represents the primary interface of a component based grid control.
57664  * <br><br>Usage:<pre><code>
57665  var grid = new Roo.grid.Grid("my-container-id", {
57666      ds: myDataStore,
57667      cm: myColModel,
57668      selModel: mySelectionModel,
57669      autoSizeColumns: true,
57670      monitorWindowResize: false,
57671      trackMouseOver: true
57672  });
57673  // set any options
57674  grid.render();
57675  * </code></pre>
57676  * <b>Common Problems:</b><br/>
57677  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57678  * element will correct this<br/>
57679  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57680  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57681  * are unpredictable.<br/>
57682  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57683  * grid to calculate dimensions/offsets.<br/>
57684   * @constructor
57685  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57686  * The container MUST have some type of size defined for the grid to fill. The container will be
57687  * automatically set to position relative if it isn't already.
57688  * @param {Object} config A config object that sets properties on this grid.
57689  */
57690 Roo.grid.Grid = function(container, config){
57691         // initialize the container
57692         this.container = Roo.get(container);
57693         this.container.update("");
57694         this.container.setStyle("overflow", "hidden");
57695     this.container.addClass('x-grid-container');
57696
57697     this.id = this.container.id;
57698
57699     Roo.apply(this, config);
57700     // check and correct shorthanded configs
57701     if(this.ds){
57702         this.dataSource = this.ds;
57703         delete this.ds;
57704     }
57705     if(this.cm){
57706         this.colModel = this.cm;
57707         delete this.cm;
57708     }
57709     if(this.sm){
57710         this.selModel = this.sm;
57711         delete this.sm;
57712     }
57713
57714     if (this.selModel) {
57715         this.selModel = Roo.factory(this.selModel, Roo.grid);
57716         this.sm = this.selModel;
57717         this.sm.xmodule = this.xmodule || false;
57718     }
57719     if (typeof(this.colModel.config) == 'undefined') {
57720         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57721         this.cm = this.colModel;
57722         this.cm.xmodule = this.xmodule || false;
57723     }
57724     if (this.dataSource) {
57725         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57726         this.ds = this.dataSource;
57727         this.ds.xmodule = this.xmodule || false;
57728          
57729     }
57730     
57731     
57732     
57733     if(this.width){
57734         this.container.setWidth(this.width);
57735     }
57736
57737     if(this.height){
57738         this.container.setHeight(this.height);
57739     }
57740     /** @private */
57741         this.addEvents({
57742         // raw events
57743         /**
57744          * @event click
57745          * The raw click event for the entire grid.
57746          * @param {Roo.EventObject} e
57747          */
57748         "click" : true,
57749         /**
57750          * @event dblclick
57751          * The raw dblclick event for the entire grid.
57752          * @param {Roo.EventObject} e
57753          */
57754         "dblclick" : true,
57755         /**
57756          * @event contextmenu
57757          * The raw contextmenu event for the entire grid.
57758          * @param {Roo.EventObject} e
57759          */
57760         "contextmenu" : true,
57761         /**
57762          * @event mousedown
57763          * The raw mousedown event for the entire grid.
57764          * @param {Roo.EventObject} e
57765          */
57766         "mousedown" : true,
57767         /**
57768          * @event mouseup
57769          * The raw mouseup event for the entire grid.
57770          * @param {Roo.EventObject} e
57771          */
57772         "mouseup" : true,
57773         /**
57774          * @event mouseover
57775          * The raw mouseover event for the entire grid.
57776          * @param {Roo.EventObject} e
57777          */
57778         "mouseover" : true,
57779         /**
57780          * @event mouseout
57781          * The raw mouseout event for the entire grid.
57782          * @param {Roo.EventObject} e
57783          */
57784         "mouseout" : true,
57785         /**
57786          * @event keypress
57787          * The raw keypress event for the entire grid.
57788          * @param {Roo.EventObject} e
57789          */
57790         "keypress" : true,
57791         /**
57792          * @event keydown
57793          * The raw keydown event for the entire grid.
57794          * @param {Roo.EventObject} e
57795          */
57796         "keydown" : true,
57797
57798         // custom events
57799
57800         /**
57801          * @event cellclick
57802          * Fires when a cell is clicked
57803          * @param {Grid} this
57804          * @param {Number} rowIndex
57805          * @param {Number} columnIndex
57806          * @param {Roo.EventObject} e
57807          */
57808         "cellclick" : true,
57809         /**
57810          * @event celldblclick
57811          * Fires when a cell is double clicked
57812          * @param {Grid} this
57813          * @param {Number} rowIndex
57814          * @param {Number} columnIndex
57815          * @param {Roo.EventObject} e
57816          */
57817         "celldblclick" : true,
57818         /**
57819          * @event rowclick
57820          * Fires when a row is clicked
57821          * @param {Grid} this
57822          * @param {Number} rowIndex
57823          * @param {Roo.EventObject} e
57824          */
57825         "rowclick" : true,
57826         /**
57827          * @event rowdblclick
57828          * Fires when a row is double clicked
57829          * @param {Grid} this
57830          * @param {Number} rowIndex
57831          * @param {Roo.EventObject} e
57832          */
57833         "rowdblclick" : true,
57834         /**
57835          * @event headerclick
57836          * Fires when a header is clicked
57837          * @param {Grid} this
57838          * @param {Number} columnIndex
57839          * @param {Roo.EventObject} e
57840          */
57841         "headerclick" : true,
57842         /**
57843          * @event headerdblclick
57844          * Fires when a header cell is double clicked
57845          * @param {Grid} this
57846          * @param {Number} columnIndex
57847          * @param {Roo.EventObject} e
57848          */
57849         "headerdblclick" : true,
57850         /**
57851          * @event rowcontextmenu
57852          * Fires when a row is right clicked
57853          * @param {Grid} this
57854          * @param {Number} rowIndex
57855          * @param {Roo.EventObject} e
57856          */
57857         "rowcontextmenu" : true,
57858         /**
57859          * @event cellcontextmenu
57860          * Fires when a cell is right clicked
57861          * @param {Grid} this
57862          * @param {Number} rowIndex
57863          * @param {Number} cellIndex
57864          * @param {Roo.EventObject} e
57865          */
57866          "cellcontextmenu" : true,
57867         /**
57868          * @event headercontextmenu
57869          * Fires when a header is right clicked
57870          * @param {Grid} this
57871          * @param {Number} columnIndex
57872          * @param {Roo.EventObject} e
57873          */
57874         "headercontextmenu" : true,
57875         /**
57876          * @event bodyscroll
57877          * Fires when the body element is scrolled
57878          * @param {Number} scrollLeft
57879          * @param {Number} scrollTop
57880          */
57881         "bodyscroll" : true,
57882         /**
57883          * @event columnresize
57884          * Fires when the user resizes a column
57885          * @param {Number} columnIndex
57886          * @param {Number} newSize
57887          */
57888         "columnresize" : true,
57889         /**
57890          * @event columnmove
57891          * Fires when the user moves a column
57892          * @param {Number} oldIndex
57893          * @param {Number} newIndex
57894          */
57895         "columnmove" : true,
57896         /**
57897          * @event startdrag
57898          * Fires when row(s) start being dragged
57899          * @param {Grid} this
57900          * @param {Roo.GridDD} dd The drag drop object
57901          * @param {event} e The raw browser event
57902          */
57903         "startdrag" : true,
57904         /**
57905          * @event enddrag
57906          * Fires when a drag operation is complete
57907          * @param {Grid} this
57908          * @param {Roo.GridDD} dd The drag drop object
57909          * @param {event} e The raw browser event
57910          */
57911         "enddrag" : true,
57912         /**
57913          * @event dragdrop
57914          * Fires when dragged row(s) are dropped on a valid DD target
57915          * @param {Grid} this
57916          * @param {Roo.GridDD} dd The drag drop object
57917          * @param {String} targetId The target drag drop object
57918          * @param {event} e The raw browser event
57919          */
57920         "dragdrop" : true,
57921         /**
57922          * @event dragover
57923          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57924          * @param {Grid} this
57925          * @param {Roo.GridDD} dd The drag drop object
57926          * @param {String} targetId The target drag drop object
57927          * @param {event} e The raw browser event
57928          */
57929         "dragover" : true,
57930         /**
57931          * @event dragenter
57932          *  Fires when the dragged row(s) first cross another DD target while being dragged
57933          * @param {Grid} this
57934          * @param {Roo.GridDD} dd The drag drop object
57935          * @param {String} targetId The target drag drop object
57936          * @param {event} e The raw browser event
57937          */
57938         "dragenter" : true,
57939         /**
57940          * @event dragout
57941          * Fires when the dragged row(s) leave another DD target while being dragged
57942          * @param {Grid} this
57943          * @param {Roo.GridDD} dd The drag drop object
57944          * @param {String} targetId The target drag drop object
57945          * @param {event} e The raw browser event
57946          */
57947         "dragout" : true,
57948         /**
57949          * @event rowclass
57950          * Fires when a row is rendered, so you can change add a style to it.
57951          * @param {GridView} gridview   The grid view
57952          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57953          */
57954         'rowclass' : true,
57955
57956         /**
57957          * @event render
57958          * Fires when the grid is rendered
57959          * @param {Grid} grid
57960          */
57961         'render' : true
57962     });
57963
57964     Roo.grid.Grid.superclass.constructor.call(this);
57965 };
57966 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57967     
57968     /**
57969          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57970          */
57971         /**
57972          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57973          */
57974         /**
57975          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57976          */
57977         /**
57978          * @cfg {Roo.grid.Store} ds The data store for the grid
57979          */
57980         /**
57981          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57982          */
57983         /**
57984      * @cfg {String} ddGroup - drag drop group.
57985      */
57986       /**
57987      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57988      */
57989
57990     /**
57991      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57992      */
57993     minColumnWidth : 25,
57994
57995     /**
57996      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57997      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57998      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57999      */
58000     autoSizeColumns : false,
58001
58002     /**
58003      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58004      */
58005     autoSizeHeaders : true,
58006
58007     /**
58008      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58009      */
58010     monitorWindowResize : true,
58011
58012     /**
58013      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58014      * rows measured to get a columns size. Default is 0 (all rows).
58015      */
58016     maxRowsToMeasure : 0,
58017
58018     /**
58019      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58020      */
58021     trackMouseOver : true,
58022
58023     /**
58024     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58025     */
58026       /**
58027     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58028     */
58029     
58030     /**
58031     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58032     */
58033     enableDragDrop : false,
58034     
58035     /**
58036     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58037     */
58038     enableColumnMove : true,
58039     
58040     /**
58041     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58042     */
58043     enableColumnHide : true,
58044     
58045     /**
58046     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58047     */
58048     enableRowHeightSync : false,
58049     
58050     /**
58051     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58052     */
58053     stripeRows : true,
58054     
58055     /**
58056     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58057     */
58058     autoHeight : false,
58059
58060     /**
58061      * @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.
58062      */
58063     autoExpandColumn : false,
58064
58065     /**
58066     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58067     * Default is 50.
58068     */
58069     autoExpandMin : 50,
58070
58071     /**
58072     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58073     */
58074     autoExpandMax : 1000,
58075
58076     /**
58077     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58078     */
58079     view : null,
58080
58081     /**
58082     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58083     */
58084     loadMask : false,
58085     /**
58086     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58087     */
58088     dropTarget: false,
58089      /**
58090     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58091     */ 
58092     sortColMenu : false,
58093     
58094     // private
58095     rendered : false,
58096
58097     /**
58098     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58099     * of a fixed width. Default is false.
58100     */
58101     /**
58102     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58103     */
58104     
58105     
58106     /**
58107     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58108     * %0 is replaced with the number of selected rows.
58109     */
58110     ddText : "{0} selected row{1}",
58111     
58112     
58113     /**
58114      * Called once after all setup has been completed and the grid is ready to be rendered.
58115      * @return {Roo.grid.Grid} this
58116      */
58117     render : function()
58118     {
58119         var c = this.container;
58120         // try to detect autoHeight/width mode
58121         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58122             this.autoHeight = true;
58123         }
58124         var view = this.getView();
58125         view.init(this);
58126
58127         c.on("click", this.onClick, this);
58128         c.on("dblclick", this.onDblClick, this);
58129         c.on("contextmenu", this.onContextMenu, this);
58130         c.on("keydown", this.onKeyDown, this);
58131         if (Roo.isTouch) {
58132             c.on("touchstart", this.onTouchStart, this);
58133         }
58134
58135         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58136
58137         this.getSelectionModel().init(this);
58138
58139         view.render();
58140
58141         if(this.loadMask){
58142             this.loadMask = new Roo.LoadMask(this.container,
58143                     Roo.apply({store:this.dataSource}, this.loadMask));
58144         }
58145         
58146         
58147         if (this.toolbar && this.toolbar.xtype) {
58148             this.toolbar.container = this.getView().getHeaderPanel(true);
58149             this.toolbar = new Roo.Toolbar(this.toolbar);
58150         }
58151         if (this.footer && this.footer.xtype) {
58152             this.footer.dataSource = this.getDataSource();
58153             this.footer.container = this.getView().getFooterPanel(true);
58154             this.footer = Roo.factory(this.footer, Roo);
58155         }
58156         if (this.dropTarget && this.dropTarget.xtype) {
58157             delete this.dropTarget.xtype;
58158             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58159         }
58160         
58161         
58162         this.rendered = true;
58163         this.fireEvent('render', this);
58164         return this;
58165     },
58166
58167     /**
58168      * Reconfigures the grid to use a different Store and Column Model.
58169      * The View will be bound to the new objects and refreshed.
58170      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58171      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58172      */
58173     reconfigure : function(dataSource, colModel){
58174         if(this.loadMask){
58175             this.loadMask.destroy();
58176             this.loadMask = new Roo.LoadMask(this.container,
58177                     Roo.apply({store:dataSource}, this.loadMask));
58178         }
58179         this.view.bind(dataSource, colModel);
58180         this.dataSource = dataSource;
58181         this.colModel = colModel;
58182         this.view.refresh(true);
58183     },
58184     /**
58185      * addColumns
58186      * Add's a column, default at the end..
58187      
58188      * @param {int} position to add (default end)
58189      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58190      */
58191     addColumns : function(pos, ar)
58192     {
58193         
58194         for (var i =0;i< ar.length;i++) {
58195             var cfg = ar[i];
58196             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58197             this.cm.lookup[cfg.id] = cfg;
58198         }
58199         
58200         
58201         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58202             pos = this.cm.config.length; //this.cm.config.push(cfg);
58203         } 
58204         pos = Math.max(0,pos);
58205         ar.unshift(0);
58206         ar.unshift(pos);
58207         this.cm.config.splice.apply(this.cm.config, ar);
58208         
58209         
58210         
58211         this.view.generateRules(this.cm);
58212         this.view.refresh(true);
58213         
58214     },
58215     
58216     
58217     
58218     
58219     // private
58220     onKeyDown : function(e){
58221         this.fireEvent("keydown", e);
58222     },
58223
58224     /**
58225      * Destroy this grid.
58226      * @param {Boolean} removeEl True to remove the element
58227      */
58228     destroy : function(removeEl, keepListeners){
58229         if(this.loadMask){
58230             this.loadMask.destroy();
58231         }
58232         var c = this.container;
58233         c.removeAllListeners();
58234         this.view.destroy();
58235         this.colModel.purgeListeners();
58236         if(!keepListeners){
58237             this.purgeListeners();
58238         }
58239         c.update("");
58240         if(removeEl === true){
58241             c.remove();
58242         }
58243     },
58244
58245     // private
58246     processEvent : function(name, e){
58247         // does this fire select???
58248         //Roo.log('grid:processEvent '  + name);
58249         
58250         if (name != 'touchstart' ) {
58251             this.fireEvent(name, e);    
58252         }
58253         
58254         var t = e.getTarget();
58255         var v = this.view;
58256         var header = v.findHeaderIndex(t);
58257         if(header !== false){
58258             var ename = name == 'touchstart' ? 'click' : name;
58259              
58260             this.fireEvent("header" + ename, this, header, e);
58261         }else{
58262             var row = v.findRowIndex(t);
58263             var cell = v.findCellIndex(t);
58264             if (name == 'touchstart') {
58265                 // first touch is always a click.
58266                 // hopefull this happens after selection is updated.?
58267                 name = false;
58268                 
58269                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58270                     var cs = this.selModel.getSelectedCell();
58271                     if (row == cs[0] && cell == cs[1]){
58272                         name = 'dblclick';
58273                     }
58274                 }
58275                 if (typeof(this.selModel.getSelections) != 'undefined') {
58276                     var cs = this.selModel.getSelections();
58277                     var ds = this.dataSource;
58278                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58279                         name = 'dblclick';
58280                     }
58281                 }
58282                 if (!name) {
58283                     return;
58284                 }
58285             }
58286             
58287             
58288             if(row !== false){
58289                 this.fireEvent("row" + name, this, row, e);
58290                 if(cell !== false){
58291                     this.fireEvent("cell" + name, this, row, cell, e);
58292                 }
58293             }
58294         }
58295     },
58296
58297     // private
58298     onClick : function(e){
58299         this.processEvent("click", e);
58300     },
58301    // private
58302     onTouchStart : function(e){
58303         this.processEvent("touchstart", e);
58304     },
58305
58306     // private
58307     onContextMenu : function(e, t){
58308         this.processEvent("contextmenu", e);
58309     },
58310
58311     // private
58312     onDblClick : function(e){
58313         this.processEvent("dblclick", e);
58314     },
58315
58316     // private
58317     walkCells : function(row, col, step, fn, scope){
58318         var cm = this.colModel, clen = cm.getColumnCount();
58319         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58320         if(step < 0){
58321             if(col < 0){
58322                 row--;
58323                 first = false;
58324             }
58325             while(row >= 0){
58326                 if(!first){
58327                     col = clen-1;
58328                 }
58329                 first = false;
58330                 while(col >= 0){
58331                     if(fn.call(scope || this, row, col, cm) === true){
58332                         return [row, col];
58333                     }
58334                     col--;
58335                 }
58336                 row--;
58337             }
58338         } else {
58339             if(col >= clen){
58340                 row++;
58341                 first = false;
58342             }
58343             while(row < rlen){
58344                 if(!first){
58345                     col = 0;
58346                 }
58347                 first = false;
58348                 while(col < clen){
58349                     if(fn.call(scope || this, row, col, cm) === true){
58350                         return [row, col];
58351                     }
58352                     col++;
58353                 }
58354                 row++;
58355             }
58356         }
58357         return null;
58358     },
58359
58360     // private
58361     getSelections : function(){
58362         return this.selModel.getSelections();
58363     },
58364
58365     /**
58366      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58367      * but if manual update is required this method will initiate it.
58368      */
58369     autoSize : function(){
58370         if(this.rendered){
58371             this.view.layout();
58372             if(this.view.adjustForScroll){
58373                 this.view.adjustForScroll();
58374             }
58375         }
58376     },
58377
58378     /**
58379      * Returns the grid's underlying element.
58380      * @return {Element} The element
58381      */
58382     getGridEl : function(){
58383         return this.container;
58384     },
58385
58386     // private for compatibility, overridden by editor grid
58387     stopEditing : function(){},
58388
58389     /**
58390      * Returns the grid's SelectionModel.
58391      * @return {SelectionModel}
58392      */
58393     getSelectionModel : function(){
58394         if(!this.selModel){
58395             this.selModel = new Roo.grid.RowSelectionModel();
58396         }
58397         return this.selModel;
58398     },
58399
58400     /**
58401      * Returns the grid's DataSource.
58402      * @return {DataSource}
58403      */
58404     getDataSource : function(){
58405         return this.dataSource;
58406     },
58407
58408     /**
58409      * Returns the grid's ColumnModel.
58410      * @return {ColumnModel}
58411      */
58412     getColumnModel : function(){
58413         return this.colModel;
58414     },
58415
58416     /**
58417      * Returns the grid's GridView object.
58418      * @return {GridView}
58419      */
58420     getView : function(){
58421         if(!this.view){
58422             this.view = new Roo.grid.GridView(this.viewConfig);
58423             this.relayEvents(this.view, [
58424                 "beforerowremoved", "beforerowsinserted",
58425                 "beforerefresh", "rowremoved",
58426                 "rowsinserted", "rowupdated" ,"refresh"
58427             ]);
58428         }
58429         return this.view;
58430     },
58431     /**
58432      * Called to get grid's drag proxy text, by default returns this.ddText.
58433      * Override this to put something different in the dragged text.
58434      * @return {String}
58435      */
58436     getDragDropText : function(){
58437         var count = this.selModel.getCount();
58438         return String.format(this.ddText, count, count == 1 ? '' : 's');
58439     }
58440 });
58441 /*
58442  * Based on:
58443  * Ext JS Library 1.1.1
58444  * Copyright(c) 2006-2007, Ext JS, LLC.
58445  *
58446  * Originally Released Under LGPL - original licence link has changed is not relivant.
58447  *
58448  * Fork - LGPL
58449  * <script type="text/javascript">
58450  */
58451  /**
58452  * @class Roo.grid.AbstractGridView
58453  * @extends Roo.util.Observable
58454  * @abstract
58455  * Abstract base class for grid Views
58456  * @constructor
58457  */
58458 Roo.grid.AbstractGridView = function(){
58459         this.grid = null;
58460         
58461         this.events = {
58462             "beforerowremoved" : true,
58463             "beforerowsinserted" : true,
58464             "beforerefresh" : true,
58465             "rowremoved" : true,
58466             "rowsinserted" : true,
58467             "rowupdated" : true,
58468             "refresh" : true
58469         };
58470     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58471 };
58472
58473 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58474     rowClass : "x-grid-row",
58475     cellClass : "x-grid-cell",
58476     tdClass : "x-grid-td",
58477     hdClass : "x-grid-hd",
58478     splitClass : "x-grid-hd-split",
58479     
58480     init: function(grid){
58481         this.grid = grid;
58482                 var cid = this.grid.getGridEl().id;
58483         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58484         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58485         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58486         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58487         },
58488         
58489     getColumnRenderers : function(){
58490         var renderers = [];
58491         var cm = this.grid.colModel;
58492         var colCount = cm.getColumnCount();
58493         for(var i = 0; i < colCount; i++){
58494             renderers[i] = cm.getRenderer(i);
58495         }
58496         return renderers;
58497     },
58498     
58499     getColumnIds : function(){
58500         var ids = [];
58501         var cm = this.grid.colModel;
58502         var colCount = cm.getColumnCount();
58503         for(var i = 0; i < colCount; i++){
58504             ids[i] = cm.getColumnId(i);
58505         }
58506         return ids;
58507     },
58508     
58509     getDataIndexes : function(){
58510         if(!this.indexMap){
58511             this.indexMap = this.buildIndexMap();
58512         }
58513         return this.indexMap.colToData;
58514     },
58515     
58516     getColumnIndexByDataIndex : function(dataIndex){
58517         if(!this.indexMap){
58518             this.indexMap = this.buildIndexMap();
58519         }
58520         return this.indexMap.dataToCol[dataIndex];
58521     },
58522     
58523     /**
58524      * Set a css style for a column dynamically. 
58525      * @param {Number} colIndex The index of the column
58526      * @param {String} name The css property name
58527      * @param {String} value The css value
58528      */
58529     setCSSStyle : function(colIndex, name, value){
58530         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58531         Roo.util.CSS.updateRule(selector, name, value);
58532     },
58533     
58534     generateRules : function(cm){
58535         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58536         Roo.util.CSS.removeStyleSheet(rulesId);
58537         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58538             var cid = cm.getColumnId(i);
58539             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58540                          this.tdSelector, cid, " {\n}\n",
58541                          this.hdSelector, cid, " {\n}\n",
58542                          this.splitSelector, cid, " {\n}\n");
58543         }
58544         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58545     }
58546 });/*
58547  * Based on:
58548  * Ext JS Library 1.1.1
58549  * Copyright(c) 2006-2007, Ext JS, LLC.
58550  *
58551  * Originally Released Under LGPL - original licence link has changed is not relivant.
58552  *
58553  * Fork - LGPL
58554  * <script type="text/javascript">
58555  */
58556
58557 // private
58558 // This is a support class used internally by the Grid components
58559 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58560     this.grid = grid;
58561     this.view = grid.getView();
58562     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58563     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58564     if(hd2){
58565         this.setHandleElId(Roo.id(hd));
58566         this.setOuterHandleElId(Roo.id(hd2));
58567     }
58568     this.scroll = false;
58569 };
58570 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58571     maxDragWidth: 120,
58572     getDragData : function(e){
58573         var t = Roo.lib.Event.getTarget(e);
58574         var h = this.view.findHeaderCell(t);
58575         if(h){
58576             return {ddel: h.firstChild, header:h};
58577         }
58578         return false;
58579     },
58580
58581     onInitDrag : function(e){
58582         this.view.headersDisabled = true;
58583         var clone = this.dragData.ddel.cloneNode(true);
58584         clone.id = Roo.id();
58585         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58586         this.proxy.update(clone);
58587         return true;
58588     },
58589
58590     afterValidDrop : function(){
58591         var v = this.view;
58592         setTimeout(function(){
58593             v.headersDisabled = false;
58594         }, 50);
58595     },
58596
58597     afterInvalidDrop : function(){
58598         var v = this.view;
58599         setTimeout(function(){
58600             v.headersDisabled = false;
58601         }, 50);
58602     }
58603 });
58604 /*
58605  * Based on:
58606  * Ext JS Library 1.1.1
58607  * Copyright(c) 2006-2007, Ext JS, LLC.
58608  *
58609  * Originally Released Under LGPL - original licence link has changed is not relivant.
58610  *
58611  * Fork - LGPL
58612  * <script type="text/javascript">
58613  */
58614 // private
58615 // This is a support class used internally by the Grid components
58616 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58617     this.grid = grid;
58618     this.view = grid.getView();
58619     // split the proxies so they don't interfere with mouse events
58620     this.proxyTop = Roo.DomHelper.append(document.body, {
58621         cls:"col-move-top", html:"&#160;"
58622     }, true);
58623     this.proxyBottom = Roo.DomHelper.append(document.body, {
58624         cls:"col-move-bottom", html:"&#160;"
58625     }, true);
58626     this.proxyTop.hide = this.proxyBottom.hide = function(){
58627         this.setLeftTop(-100,-100);
58628         this.setStyle("visibility", "hidden");
58629     };
58630     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58631     // temporarily disabled
58632     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58633     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58634 };
58635 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58636     proxyOffsets : [-4, -9],
58637     fly: Roo.Element.fly,
58638
58639     getTargetFromEvent : function(e){
58640         var t = Roo.lib.Event.getTarget(e);
58641         var cindex = this.view.findCellIndex(t);
58642         if(cindex !== false){
58643             return this.view.getHeaderCell(cindex);
58644         }
58645         return null;
58646     },
58647
58648     nextVisible : function(h){
58649         var v = this.view, cm = this.grid.colModel;
58650         h = h.nextSibling;
58651         while(h){
58652             if(!cm.isHidden(v.getCellIndex(h))){
58653                 return h;
58654             }
58655             h = h.nextSibling;
58656         }
58657         return null;
58658     },
58659
58660     prevVisible : function(h){
58661         var v = this.view, cm = this.grid.colModel;
58662         h = h.prevSibling;
58663         while(h){
58664             if(!cm.isHidden(v.getCellIndex(h))){
58665                 return h;
58666             }
58667             h = h.prevSibling;
58668         }
58669         return null;
58670     },
58671
58672     positionIndicator : function(h, n, e){
58673         var x = Roo.lib.Event.getPageX(e);
58674         var r = Roo.lib.Dom.getRegion(n.firstChild);
58675         var px, pt, py = r.top + this.proxyOffsets[1];
58676         if((r.right - x) <= (r.right-r.left)/2){
58677             px = r.right+this.view.borderWidth;
58678             pt = "after";
58679         }else{
58680             px = r.left;
58681             pt = "before";
58682         }
58683         var oldIndex = this.view.getCellIndex(h);
58684         var newIndex = this.view.getCellIndex(n);
58685
58686         if(this.grid.colModel.isFixed(newIndex)){
58687             return false;
58688         }
58689
58690         var locked = this.grid.colModel.isLocked(newIndex);
58691
58692         if(pt == "after"){
58693             newIndex++;
58694         }
58695         if(oldIndex < newIndex){
58696             newIndex--;
58697         }
58698         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58699             return false;
58700         }
58701         px +=  this.proxyOffsets[0];
58702         this.proxyTop.setLeftTop(px, py);
58703         this.proxyTop.show();
58704         if(!this.bottomOffset){
58705             this.bottomOffset = this.view.mainHd.getHeight();
58706         }
58707         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58708         this.proxyBottom.show();
58709         return pt;
58710     },
58711
58712     onNodeEnter : function(n, dd, e, data){
58713         if(data.header != n){
58714             this.positionIndicator(data.header, n, e);
58715         }
58716     },
58717
58718     onNodeOver : function(n, dd, e, data){
58719         var result = false;
58720         if(data.header != n){
58721             result = this.positionIndicator(data.header, n, e);
58722         }
58723         if(!result){
58724             this.proxyTop.hide();
58725             this.proxyBottom.hide();
58726         }
58727         return result ? this.dropAllowed : this.dropNotAllowed;
58728     },
58729
58730     onNodeOut : function(n, dd, e, data){
58731         this.proxyTop.hide();
58732         this.proxyBottom.hide();
58733     },
58734
58735     onNodeDrop : function(n, dd, e, data){
58736         var h = data.header;
58737         if(h != n){
58738             var cm = this.grid.colModel;
58739             var x = Roo.lib.Event.getPageX(e);
58740             var r = Roo.lib.Dom.getRegion(n.firstChild);
58741             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58742             var oldIndex = this.view.getCellIndex(h);
58743             var newIndex = this.view.getCellIndex(n);
58744             var locked = cm.isLocked(newIndex);
58745             if(pt == "after"){
58746                 newIndex++;
58747             }
58748             if(oldIndex < newIndex){
58749                 newIndex--;
58750             }
58751             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58752                 return false;
58753             }
58754             cm.setLocked(oldIndex, locked, true);
58755             cm.moveColumn(oldIndex, newIndex);
58756             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58757             return true;
58758         }
58759         return false;
58760     }
58761 });
58762 /*
58763  * Based on:
58764  * Ext JS Library 1.1.1
58765  * Copyright(c) 2006-2007, Ext JS, LLC.
58766  *
58767  * Originally Released Under LGPL - original licence link has changed is not relivant.
58768  *
58769  * Fork - LGPL
58770  * <script type="text/javascript">
58771  */
58772   
58773 /**
58774  * @class Roo.grid.GridView
58775  * @extends Roo.util.Observable
58776  *
58777  * @constructor
58778  * @param {Object} config
58779  */
58780 Roo.grid.GridView = function(config){
58781     Roo.grid.GridView.superclass.constructor.call(this);
58782     this.el = null;
58783
58784     Roo.apply(this, config);
58785 };
58786
58787 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58788
58789     unselectable :  'unselectable="on"',
58790     unselectableCls :  'x-unselectable',
58791     
58792     
58793     rowClass : "x-grid-row",
58794
58795     cellClass : "x-grid-col",
58796
58797     tdClass : "x-grid-td",
58798
58799     hdClass : "x-grid-hd",
58800
58801     splitClass : "x-grid-split",
58802
58803     sortClasses : ["sort-asc", "sort-desc"],
58804
58805     enableMoveAnim : false,
58806
58807     hlColor: "C3DAF9",
58808
58809     dh : Roo.DomHelper,
58810
58811     fly : Roo.Element.fly,
58812
58813     css : Roo.util.CSS,
58814
58815     borderWidth: 1,
58816
58817     splitOffset: 3,
58818
58819     scrollIncrement : 22,
58820
58821     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58822
58823     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58824
58825     bind : function(ds, cm){
58826         if(this.ds){
58827             this.ds.un("load", this.onLoad, this);
58828             this.ds.un("datachanged", this.onDataChange, this);
58829             this.ds.un("add", this.onAdd, this);
58830             this.ds.un("remove", this.onRemove, this);
58831             this.ds.un("update", this.onUpdate, this);
58832             this.ds.un("clear", this.onClear, this);
58833         }
58834         if(ds){
58835             ds.on("load", this.onLoad, this);
58836             ds.on("datachanged", this.onDataChange, this);
58837             ds.on("add", this.onAdd, this);
58838             ds.on("remove", this.onRemove, this);
58839             ds.on("update", this.onUpdate, this);
58840             ds.on("clear", this.onClear, this);
58841         }
58842         this.ds = ds;
58843
58844         if(this.cm){
58845             this.cm.un("widthchange", this.onColWidthChange, this);
58846             this.cm.un("headerchange", this.onHeaderChange, this);
58847             this.cm.un("hiddenchange", this.onHiddenChange, this);
58848             this.cm.un("columnmoved", this.onColumnMove, this);
58849             this.cm.un("columnlockchange", this.onColumnLock, this);
58850         }
58851         if(cm){
58852             this.generateRules(cm);
58853             cm.on("widthchange", this.onColWidthChange, this);
58854             cm.on("headerchange", this.onHeaderChange, this);
58855             cm.on("hiddenchange", this.onHiddenChange, this);
58856             cm.on("columnmoved", this.onColumnMove, this);
58857             cm.on("columnlockchange", this.onColumnLock, this);
58858         }
58859         this.cm = cm;
58860     },
58861
58862     init: function(grid){
58863         Roo.grid.GridView.superclass.init.call(this, grid);
58864
58865         this.bind(grid.dataSource, grid.colModel);
58866
58867         grid.on("headerclick", this.handleHeaderClick, this);
58868
58869         if(grid.trackMouseOver){
58870             grid.on("mouseover", this.onRowOver, this);
58871             grid.on("mouseout", this.onRowOut, this);
58872         }
58873         grid.cancelTextSelection = function(){};
58874         this.gridId = grid.id;
58875
58876         var tpls = this.templates || {};
58877
58878         if(!tpls.master){
58879             tpls.master = new Roo.Template(
58880                '<div class="x-grid" hidefocus="true">',
58881                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58882                   '<div class="x-grid-topbar"></div>',
58883                   '<div class="x-grid-scroller"><div></div></div>',
58884                   '<div class="x-grid-locked">',
58885                       '<div class="x-grid-header">{lockedHeader}</div>',
58886                       '<div class="x-grid-body">{lockedBody}</div>',
58887                   "</div>",
58888                   '<div class="x-grid-viewport">',
58889                       '<div class="x-grid-header">{header}</div>',
58890                       '<div class="x-grid-body">{body}</div>',
58891                   "</div>",
58892                   '<div class="x-grid-bottombar"></div>',
58893                  
58894                   '<div class="x-grid-resize-proxy">&#160;</div>',
58895                "</div>"
58896             );
58897             tpls.master.disableformats = true;
58898         }
58899
58900         if(!tpls.header){
58901             tpls.header = new Roo.Template(
58902                '<table border="0" cellspacing="0" cellpadding="0">',
58903                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58904                "</table>{splits}"
58905             );
58906             tpls.header.disableformats = true;
58907         }
58908         tpls.header.compile();
58909
58910         if(!tpls.hcell){
58911             tpls.hcell = new Roo.Template(
58912                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58913                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58914                 "</div></td>"
58915              );
58916              tpls.hcell.disableFormats = true;
58917         }
58918         tpls.hcell.compile();
58919
58920         if(!tpls.hsplit){
58921             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58922                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58923             tpls.hsplit.disableFormats = true;
58924         }
58925         tpls.hsplit.compile();
58926
58927         if(!tpls.body){
58928             tpls.body = new Roo.Template(
58929                '<table border="0" cellspacing="0" cellpadding="0">',
58930                "<tbody>{rows}</tbody>",
58931                "</table>"
58932             );
58933             tpls.body.disableFormats = true;
58934         }
58935         tpls.body.compile();
58936
58937         if(!tpls.row){
58938             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58939             tpls.row.disableFormats = true;
58940         }
58941         tpls.row.compile();
58942
58943         if(!tpls.cell){
58944             tpls.cell = new Roo.Template(
58945                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58946                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58947                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58948                 "</td>"
58949             );
58950             tpls.cell.disableFormats = true;
58951         }
58952         tpls.cell.compile();
58953
58954         this.templates = tpls;
58955     },
58956
58957     // remap these for backwards compat
58958     onColWidthChange : function(){
58959         this.updateColumns.apply(this, arguments);
58960     },
58961     onHeaderChange : function(){
58962         this.updateHeaders.apply(this, arguments);
58963     }, 
58964     onHiddenChange : function(){
58965         this.handleHiddenChange.apply(this, arguments);
58966     },
58967     onColumnMove : function(){
58968         this.handleColumnMove.apply(this, arguments);
58969     },
58970     onColumnLock : function(){
58971         this.handleLockChange.apply(this, arguments);
58972     },
58973
58974     onDataChange : function(){
58975         this.refresh();
58976         this.updateHeaderSortState();
58977     },
58978
58979     onClear : function(){
58980         this.refresh();
58981     },
58982
58983     onUpdate : function(ds, record){
58984         this.refreshRow(record);
58985     },
58986
58987     refreshRow : function(record){
58988         var ds = this.ds, index;
58989         if(typeof record == 'number'){
58990             index = record;
58991             record = ds.getAt(index);
58992         }else{
58993             index = ds.indexOf(record);
58994         }
58995         this.insertRows(ds, index, index, true);
58996         this.onRemove(ds, record, index+1, true);
58997         this.syncRowHeights(index, index);
58998         this.layout();
58999         this.fireEvent("rowupdated", this, index, record);
59000     },
59001
59002     onAdd : function(ds, records, index){
59003         this.insertRows(ds, index, index + (records.length-1));
59004     },
59005
59006     onRemove : function(ds, record, index, isUpdate){
59007         if(isUpdate !== true){
59008             this.fireEvent("beforerowremoved", this, index, record);
59009         }
59010         var bt = this.getBodyTable(), lt = this.getLockedTable();
59011         if(bt.rows[index]){
59012             bt.firstChild.removeChild(bt.rows[index]);
59013         }
59014         if(lt.rows[index]){
59015             lt.firstChild.removeChild(lt.rows[index]);
59016         }
59017         if(isUpdate !== true){
59018             this.stripeRows(index);
59019             this.syncRowHeights(index, index);
59020             this.layout();
59021             this.fireEvent("rowremoved", this, index, record);
59022         }
59023     },
59024
59025     onLoad : function(){
59026         this.scrollToTop();
59027     },
59028
59029     /**
59030      * Scrolls the grid to the top
59031      */
59032     scrollToTop : function(){
59033         if(this.scroller){
59034             this.scroller.dom.scrollTop = 0;
59035             this.syncScroll();
59036         }
59037     },
59038
59039     /**
59040      * Gets a panel in the header of the grid that can be used for toolbars etc.
59041      * After modifying the contents of this panel a call to grid.autoSize() may be
59042      * required to register any changes in size.
59043      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59044      * @return Roo.Element
59045      */
59046     getHeaderPanel : function(doShow){
59047         if(doShow){
59048             this.headerPanel.show();
59049         }
59050         return this.headerPanel;
59051     },
59052
59053     /**
59054      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59055      * After modifying the contents of this panel a call to grid.autoSize() may be
59056      * required to register any changes in size.
59057      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59058      * @return Roo.Element
59059      */
59060     getFooterPanel : function(doShow){
59061         if(doShow){
59062             this.footerPanel.show();
59063         }
59064         return this.footerPanel;
59065     },
59066
59067     initElements : function(){
59068         var E = Roo.Element;
59069         var el = this.grid.getGridEl().dom.firstChild;
59070         var cs = el.childNodes;
59071
59072         this.el = new E(el);
59073         
59074          this.focusEl = new E(el.firstChild);
59075         this.focusEl.swallowEvent("click", true);
59076         
59077         this.headerPanel = new E(cs[1]);
59078         this.headerPanel.enableDisplayMode("block");
59079
59080         this.scroller = new E(cs[2]);
59081         this.scrollSizer = new E(this.scroller.dom.firstChild);
59082
59083         this.lockedWrap = new E(cs[3]);
59084         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59085         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59086
59087         this.mainWrap = new E(cs[4]);
59088         this.mainHd = new E(this.mainWrap.dom.firstChild);
59089         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59090
59091         this.footerPanel = new E(cs[5]);
59092         this.footerPanel.enableDisplayMode("block");
59093
59094         this.resizeProxy = new E(cs[6]);
59095
59096         this.headerSelector = String.format(
59097            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59098            this.lockedHd.id, this.mainHd.id
59099         );
59100
59101         this.splitterSelector = String.format(
59102            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59103            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59104         );
59105     },
59106     idToCssName : function(s)
59107     {
59108         return s.replace(/[^a-z0-9]+/ig, '-');
59109     },
59110
59111     getHeaderCell : function(index){
59112         return Roo.DomQuery.select(this.headerSelector)[index];
59113     },
59114
59115     getHeaderCellMeasure : function(index){
59116         return this.getHeaderCell(index).firstChild;
59117     },
59118
59119     getHeaderCellText : function(index){
59120         return this.getHeaderCell(index).firstChild.firstChild;
59121     },
59122
59123     getLockedTable : function(){
59124         return this.lockedBody.dom.firstChild;
59125     },
59126
59127     getBodyTable : function(){
59128         return this.mainBody.dom.firstChild;
59129     },
59130
59131     getLockedRow : function(index){
59132         return this.getLockedTable().rows[index];
59133     },
59134
59135     getRow : function(index){
59136         return this.getBodyTable().rows[index];
59137     },
59138
59139     getRowComposite : function(index){
59140         if(!this.rowEl){
59141             this.rowEl = new Roo.CompositeElementLite();
59142         }
59143         var els = [], lrow, mrow;
59144         if(lrow = this.getLockedRow(index)){
59145             els.push(lrow);
59146         }
59147         if(mrow = this.getRow(index)){
59148             els.push(mrow);
59149         }
59150         this.rowEl.elements = els;
59151         return this.rowEl;
59152     },
59153     /**
59154      * Gets the 'td' of the cell
59155      * 
59156      * @param {Integer} rowIndex row to select
59157      * @param {Integer} colIndex column to select
59158      * 
59159      * @return {Object} 
59160      */
59161     getCell : function(rowIndex, colIndex){
59162         var locked = this.cm.getLockedCount();
59163         var source;
59164         if(colIndex < locked){
59165             source = this.lockedBody.dom.firstChild;
59166         }else{
59167             source = this.mainBody.dom.firstChild;
59168             colIndex -= locked;
59169         }
59170         return source.rows[rowIndex].childNodes[colIndex];
59171     },
59172
59173     getCellText : function(rowIndex, colIndex){
59174         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59175     },
59176
59177     getCellBox : function(cell){
59178         var b = this.fly(cell).getBox();
59179         if(Roo.isOpera){ // opera fails to report the Y
59180             b.y = cell.offsetTop + this.mainBody.getY();
59181         }
59182         return b;
59183     },
59184
59185     getCellIndex : function(cell){
59186         var id = String(cell.className).match(this.cellRE);
59187         if(id){
59188             return parseInt(id[1], 10);
59189         }
59190         return 0;
59191     },
59192
59193     findHeaderIndex : function(n){
59194         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59195         return r ? this.getCellIndex(r) : false;
59196     },
59197
59198     findHeaderCell : function(n){
59199         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59200         return r ? r : false;
59201     },
59202
59203     findRowIndex : function(n){
59204         if(!n){
59205             return false;
59206         }
59207         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59208         return r ? r.rowIndex : false;
59209     },
59210
59211     findCellIndex : function(node){
59212         var stop = this.el.dom;
59213         while(node && node != stop){
59214             if(this.findRE.test(node.className)){
59215                 return this.getCellIndex(node);
59216             }
59217             node = node.parentNode;
59218         }
59219         return false;
59220     },
59221
59222     getColumnId : function(index){
59223         return this.cm.getColumnId(index);
59224     },
59225
59226     getSplitters : function()
59227     {
59228         if(this.splitterSelector){
59229            return Roo.DomQuery.select(this.splitterSelector);
59230         }else{
59231             return null;
59232       }
59233     },
59234
59235     getSplitter : function(index){
59236         return this.getSplitters()[index];
59237     },
59238
59239     onRowOver : function(e, t){
59240         var row;
59241         if((row = this.findRowIndex(t)) !== false){
59242             this.getRowComposite(row).addClass("x-grid-row-over");
59243         }
59244     },
59245
59246     onRowOut : function(e, t){
59247         var row;
59248         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59249             this.getRowComposite(row).removeClass("x-grid-row-over");
59250         }
59251     },
59252
59253     renderHeaders : function(){
59254         var cm = this.cm;
59255         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59256         var cb = [], lb = [], sb = [], lsb = [], p = {};
59257         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59258             p.cellId = "x-grid-hd-0-" + i;
59259             p.splitId = "x-grid-csplit-0-" + i;
59260             p.id = cm.getColumnId(i);
59261             p.value = cm.getColumnHeader(i) || "";
59262             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59263             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59264             if(!cm.isLocked(i)){
59265                 cb[cb.length] = ct.apply(p);
59266                 sb[sb.length] = st.apply(p);
59267             }else{
59268                 lb[lb.length] = ct.apply(p);
59269                 lsb[lsb.length] = st.apply(p);
59270             }
59271         }
59272         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59273                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59274     },
59275
59276     updateHeaders : function(){
59277         var html = this.renderHeaders();
59278         this.lockedHd.update(html[0]);
59279         this.mainHd.update(html[1]);
59280     },
59281
59282     /**
59283      * Focuses the specified row.
59284      * @param {Number} row The row index
59285      */
59286     focusRow : function(row)
59287     {
59288         //Roo.log('GridView.focusRow');
59289         var x = this.scroller.dom.scrollLeft;
59290         this.focusCell(row, 0, false);
59291         this.scroller.dom.scrollLeft = x;
59292     },
59293
59294     /**
59295      * Focuses the specified cell.
59296      * @param {Number} row The row index
59297      * @param {Number} col The column index
59298      * @param {Boolean} hscroll false to disable horizontal scrolling
59299      */
59300     focusCell : function(row, col, hscroll)
59301     {
59302         //Roo.log('GridView.focusCell');
59303         var el = this.ensureVisible(row, col, hscroll);
59304         this.focusEl.alignTo(el, "tl-tl");
59305         if(Roo.isGecko){
59306             this.focusEl.focus();
59307         }else{
59308             this.focusEl.focus.defer(1, this.focusEl);
59309         }
59310     },
59311
59312     /**
59313      * Scrolls the specified cell into view
59314      * @param {Number} row The row index
59315      * @param {Number} col The column index
59316      * @param {Boolean} hscroll false to disable horizontal scrolling
59317      */
59318     ensureVisible : function(row, col, hscroll)
59319     {
59320         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59321         //return null; //disable for testing.
59322         if(typeof row != "number"){
59323             row = row.rowIndex;
59324         }
59325         if(row < 0 && row >= this.ds.getCount()){
59326             return  null;
59327         }
59328         col = (col !== undefined ? col : 0);
59329         var cm = this.grid.colModel;
59330         while(cm.isHidden(col)){
59331             col++;
59332         }
59333
59334         var el = this.getCell(row, col);
59335         if(!el){
59336             return null;
59337         }
59338         var c = this.scroller.dom;
59339
59340         var ctop = parseInt(el.offsetTop, 10);
59341         var cleft = parseInt(el.offsetLeft, 10);
59342         var cbot = ctop + el.offsetHeight;
59343         var cright = cleft + el.offsetWidth;
59344         
59345         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59346         var stop = parseInt(c.scrollTop, 10);
59347         var sleft = parseInt(c.scrollLeft, 10);
59348         var sbot = stop + ch;
59349         var sright = sleft + c.clientWidth;
59350         /*
59351         Roo.log('GridView.ensureVisible:' +
59352                 ' ctop:' + ctop +
59353                 ' c.clientHeight:' + c.clientHeight +
59354                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59355                 ' stop:' + stop +
59356                 ' cbot:' + cbot +
59357                 ' sbot:' + sbot +
59358                 ' ch:' + ch  
59359                 );
59360         */
59361         if(ctop < stop){
59362             c.scrollTop = ctop;
59363             //Roo.log("set scrolltop to ctop DISABLE?");
59364         }else if(cbot > sbot){
59365             //Roo.log("set scrolltop to cbot-ch");
59366             c.scrollTop = cbot-ch;
59367         }
59368         
59369         if(hscroll !== false){
59370             if(cleft < sleft){
59371                 c.scrollLeft = cleft;
59372             }else if(cright > sright){
59373                 c.scrollLeft = cright-c.clientWidth;
59374             }
59375         }
59376          
59377         return el;
59378     },
59379
59380     updateColumns : function(){
59381         this.grid.stopEditing();
59382         var cm = this.grid.colModel, colIds = this.getColumnIds();
59383         //var totalWidth = cm.getTotalWidth();
59384         var pos = 0;
59385         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59386             //if(cm.isHidden(i)) continue;
59387             var w = cm.getColumnWidth(i);
59388             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59389             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59390         }
59391         this.updateSplitters();
59392     },
59393
59394     generateRules : function(cm){
59395         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59396         Roo.util.CSS.removeStyleSheet(rulesId);
59397         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59398             var cid = cm.getColumnId(i);
59399             var align = '';
59400             if(cm.config[i].align){
59401                 align = 'text-align:'+cm.config[i].align+';';
59402             }
59403             var hidden = '';
59404             if(cm.isHidden(i)){
59405                 hidden = 'display:none;';
59406             }
59407             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59408             ruleBuf.push(
59409                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59410                     this.hdSelector, cid, " {\n", align, width, "}\n",
59411                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59412                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59413         }
59414         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59415     },
59416
59417     updateSplitters : function(){
59418         var cm = this.cm, s = this.getSplitters();
59419         if(s){ // splitters not created yet
59420             var pos = 0, locked = true;
59421             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59422                 if(cm.isHidden(i)) {
59423                     continue;
59424                 }
59425                 var w = cm.getColumnWidth(i); // make sure it's a number
59426                 if(!cm.isLocked(i) && locked){
59427                     pos = 0;
59428                     locked = false;
59429                 }
59430                 pos += w;
59431                 s[i].style.left = (pos-this.splitOffset) + "px";
59432             }
59433         }
59434     },
59435
59436     handleHiddenChange : function(colModel, colIndex, hidden){
59437         if(hidden){
59438             this.hideColumn(colIndex);
59439         }else{
59440             this.unhideColumn(colIndex);
59441         }
59442     },
59443
59444     hideColumn : function(colIndex){
59445         var cid = this.getColumnId(colIndex);
59446         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59447         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59448         if(Roo.isSafari){
59449             this.updateHeaders();
59450         }
59451         this.updateSplitters();
59452         this.layout();
59453     },
59454
59455     unhideColumn : function(colIndex){
59456         var cid = this.getColumnId(colIndex);
59457         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59458         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59459
59460         if(Roo.isSafari){
59461             this.updateHeaders();
59462         }
59463         this.updateSplitters();
59464         this.layout();
59465     },
59466
59467     insertRows : function(dm, firstRow, lastRow, isUpdate){
59468         if(firstRow == 0 && lastRow == dm.getCount()-1){
59469             this.refresh();
59470         }else{
59471             if(!isUpdate){
59472                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59473             }
59474             var s = this.getScrollState();
59475             var markup = this.renderRows(firstRow, lastRow);
59476             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59477             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59478             this.restoreScroll(s);
59479             if(!isUpdate){
59480                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59481                 this.syncRowHeights(firstRow, lastRow);
59482                 this.stripeRows(firstRow);
59483                 this.layout();
59484             }
59485         }
59486     },
59487
59488     bufferRows : function(markup, target, index){
59489         var before = null, trows = target.rows, tbody = target.tBodies[0];
59490         if(index < trows.length){
59491             before = trows[index];
59492         }
59493         var b = document.createElement("div");
59494         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59495         var rows = b.firstChild.rows;
59496         for(var i = 0, len = rows.length; i < len; i++){
59497             if(before){
59498                 tbody.insertBefore(rows[0], before);
59499             }else{
59500                 tbody.appendChild(rows[0]);
59501             }
59502         }
59503         b.innerHTML = "";
59504         b = null;
59505     },
59506
59507     deleteRows : function(dm, firstRow, lastRow){
59508         if(dm.getRowCount()<1){
59509             this.fireEvent("beforerefresh", this);
59510             this.mainBody.update("");
59511             this.lockedBody.update("");
59512             this.fireEvent("refresh", this);
59513         }else{
59514             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59515             var bt = this.getBodyTable();
59516             var tbody = bt.firstChild;
59517             var rows = bt.rows;
59518             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59519                 tbody.removeChild(rows[firstRow]);
59520             }
59521             this.stripeRows(firstRow);
59522             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59523         }
59524     },
59525
59526     updateRows : function(dataSource, firstRow, lastRow){
59527         var s = this.getScrollState();
59528         this.refresh();
59529         this.restoreScroll(s);
59530     },
59531
59532     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59533         if(!noRefresh){
59534            this.refresh();
59535         }
59536         this.updateHeaderSortState();
59537     },
59538
59539     getScrollState : function(){
59540         
59541         var sb = this.scroller.dom;
59542         return {left: sb.scrollLeft, top: sb.scrollTop};
59543     },
59544
59545     stripeRows : function(startRow){
59546         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59547             return;
59548         }
59549         startRow = startRow || 0;
59550         var rows = this.getBodyTable().rows;
59551         var lrows = this.getLockedTable().rows;
59552         var cls = ' x-grid-row-alt ';
59553         for(var i = startRow, len = rows.length; i < len; i++){
59554             var row = rows[i], lrow = lrows[i];
59555             var isAlt = ((i+1) % 2 == 0);
59556             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59557             if(isAlt == hasAlt){
59558                 continue;
59559             }
59560             if(isAlt){
59561                 row.className += " x-grid-row-alt";
59562             }else{
59563                 row.className = row.className.replace("x-grid-row-alt", "");
59564             }
59565             if(lrow){
59566                 lrow.className = row.className;
59567             }
59568         }
59569     },
59570
59571     restoreScroll : function(state){
59572         //Roo.log('GridView.restoreScroll');
59573         var sb = this.scroller.dom;
59574         sb.scrollLeft = state.left;
59575         sb.scrollTop = state.top;
59576         this.syncScroll();
59577     },
59578
59579     syncScroll : function(){
59580         //Roo.log('GridView.syncScroll');
59581         var sb = this.scroller.dom;
59582         var sh = this.mainHd.dom;
59583         var bs = this.mainBody.dom;
59584         var lv = this.lockedBody.dom;
59585         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59586         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59587     },
59588
59589     handleScroll : function(e){
59590         this.syncScroll();
59591         var sb = this.scroller.dom;
59592         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59593         e.stopEvent();
59594     },
59595
59596     handleWheel : function(e){
59597         var d = e.getWheelDelta();
59598         this.scroller.dom.scrollTop -= d*22;
59599         // set this here to prevent jumpy scrolling on large tables
59600         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59601         e.stopEvent();
59602     },
59603
59604     renderRows : function(startRow, endRow){
59605         // pull in all the crap needed to render rows
59606         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59607         var colCount = cm.getColumnCount();
59608
59609         if(ds.getCount() < 1){
59610             return ["", ""];
59611         }
59612
59613         // build a map for all the columns
59614         var cs = [];
59615         for(var i = 0; i < colCount; i++){
59616             var name = cm.getDataIndex(i);
59617             cs[i] = {
59618                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59619                 renderer : cm.getRenderer(i),
59620                 id : cm.getColumnId(i),
59621                 locked : cm.isLocked(i),
59622                 has_editor : cm.isCellEditable(i)
59623             };
59624         }
59625
59626         startRow = startRow || 0;
59627         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59628
59629         // records to render
59630         var rs = ds.getRange(startRow, endRow);
59631
59632         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59633     },
59634
59635     // As much as I hate to duplicate code, this was branched because FireFox really hates
59636     // [].join("") on strings. The performance difference was substantial enough to
59637     // branch this function
59638     doRender : Roo.isGecko ?
59639             function(cs, rs, ds, startRow, colCount, stripe){
59640                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59641                 // buffers
59642                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59643                 
59644                 var hasListener = this.grid.hasListener('rowclass');
59645                 var rowcfg = {};
59646                 for(var j = 0, len = rs.length; j < len; j++){
59647                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59648                     for(var i = 0; i < colCount; i++){
59649                         c = cs[i];
59650                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59651                         p.id = c.id;
59652                         p.css = p.attr = "";
59653                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59654                         if(p.value == undefined || p.value === "") {
59655                             p.value = "&#160;";
59656                         }
59657                         if(c.has_editor){
59658                             p.css += ' x-grid-editable-cell';
59659                         }
59660                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59661                             p.css +=  ' x-grid-dirty-cell';
59662                         }
59663                         var markup = ct.apply(p);
59664                         if(!c.locked){
59665                             cb+= markup;
59666                         }else{
59667                             lcb+= markup;
59668                         }
59669                     }
59670                     var alt = [];
59671                     if(stripe && ((rowIndex+1) % 2 == 0)){
59672                         alt.push("x-grid-row-alt")
59673                     }
59674                     if(r.dirty){
59675                         alt.push(  " x-grid-dirty-row");
59676                     }
59677                     rp.cells = lcb;
59678                     if(this.getRowClass){
59679                         alt.push(this.getRowClass(r, rowIndex));
59680                     }
59681                     if (hasListener) {
59682                         rowcfg = {
59683                              
59684                             record: r,
59685                             rowIndex : rowIndex,
59686                             rowClass : ''
59687                         };
59688                         this.grid.fireEvent('rowclass', this, rowcfg);
59689                         alt.push(rowcfg.rowClass);
59690                     }
59691                     rp.alt = alt.join(" ");
59692                     lbuf+= rt.apply(rp);
59693                     rp.cells = cb;
59694                     buf+=  rt.apply(rp);
59695                 }
59696                 return [lbuf, buf];
59697             } :
59698             function(cs, rs, ds, startRow, colCount, stripe){
59699                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59700                 // buffers
59701                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59702                 var hasListener = this.grid.hasListener('rowclass');
59703  
59704                 var rowcfg = {};
59705                 for(var j = 0, len = rs.length; j < len; j++){
59706                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59707                     for(var i = 0; i < colCount; i++){
59708                         c = cs[i];
59709                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59710                         p.id = c.id;
59711                         p.css = p.attr = "";
59712                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59713                         if(p.value == undefined || p.value === "") {
59714                             p.value = "&#160;";
59715                         }
59716                         //Roo.log(c);
59717                          if(c.has_editor){
59718                             p.css += ' x-grid-editable-cell';
59719                         }
59720                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59721                             p.css += ' x-grid-dirty-cell' 
59722                         }
59723                         
59724                         var markup = ct.apply(p);
59725                         if(!c.locked){
59726                             cb[cb.length] = markup;
59727                         }else{
59728                             lcb[lcb.length] = markup;
59729                         }
59730                     }
59731                     var alt = [];
59732                     if(stripe && ((rowIndex+1) % 2 == 0)){
59733                         alt.push( "x-grid-row-alt");
59734                     }
59735                     if(r.dirty){
59736                         alt.push(" x-grid-dirty-row");
59737                     }
59738                     rp.cells = lcb;
59739                     if(this.getRowClass){
59740                         alt.push( this.getRowClass(r, rowIndex));
59741                     }
59742                     if (hasListener) {
59743                         rowcfg = {
59744                              
59745                             record: r,
59746                             rowIndex : rowIndex,
59747                             rowClass : ''
59748                         };
59749                         this.grid.fireEvent('rowclass', this, rowcfg);
59750                         alt.push(rowcfg.rowClass);
59751                     }
59752                     
59753                     rp.alt = alt.join(" ");
59754                     rp.cells = lcb.join("");
59755                     lbuf[lbuf.length] = rt.apply(rp);
59756                     rp.cells = cb.join("");
59757                     buf[buf.length] =  rt.apply(rp);
59758                 }
59759                 return [lbuf.join(""), buf.join("")];
59760             },
59761
59762     renderBody : function(){
59763         var markup = this.renderRows();
59764         var bt = this.templates.body;
59765         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59766     },
59767
59768     /**
59769      * Refreshes the grid
59770      * @param {Boolean} headersToo
59771      */
59772     refresh : function(headersToo){
59773         this.fireEvent("beforerefresh", this);
59774         this.grid.stopEditing();
59775         var result = this.renderBody();
59776         this.lockedBody.update(result[0]);
59777         this.mainBody.update(result[1]);
59778         if(headersToo === true){
59779             this.updateHeaders();
59780             this.updateColumns();
59781             this.updateSplitters();
59782             this.updateHeaderSortState();
59783         }
59784         this.syncRowHeights();
59785         this.layout();
59786         this.fireEvent("refresh", this);
59787     },
59788
59789     handleColumnMove : function(cm, oldIndex, newIndex){
59790         this.indexMap = null;
59791         var s = this.getScrollState();
59792         this.refresh(true);
59793         this.restoreScroll(s);
59794         this.afterMove(newIndex);
59795     },
59796
59797     afterMove : function(colIndex){
59798         if(this.enableMoveAnim && Roo.enableFx){
59799             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59800         }
59801         // if multisort - fix sortOrder, and reload..
59802         if (this.grid.dataSource.multiSort) {
59803             // the we can call sort again..
59804             var dm = this.grid.dataSource;
59805             var cm = this.grid.colModel;
59806             var so = [];
59807             for(var i = 0; i < cm.config.length; i++ ) {
59808                 
59809                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59810                     continue; // dont' bother, it's not in sort list or being set.
59811                 }
59812                 
59813                 so.push(cm.config[i].dataIndex);
59814             };
59815             dm.sortOrder = so;
59816             dm.load(dm.lastOptions);
59817             
59818             
59819         }
59820         
59821     },
59822
59823     updateCell : function(dm, rowIndex, dataIndex){
59824         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59825         if(typeof colIndex == "undefined"){ // not present in grid
59826             return;
59827         }
59828         var cm = this.grid.colModel;
59829         var cell = this.getCell(rowIndex, colIndex);
59830         var cellText = this.getCellText(rowIndex, colIndex);
59831
59832         var p = {
59833             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59834             id : cm.getColumnId(colIndex),
59835             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59836         };
59837         var renderer = cm.getRenderer(colIndex);
59838         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59839         if(typeof val == "undefined" || val === "") {
59840             val = "&#160;";
59841         }
59842         cellText.innerHTML = val;
59843         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59844         this.syncRowHeights(rowIndex, rowIndex);
59845     },
59846
59847     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59848         var maxWidth = 0;
59849         if(this.grid.autoSizeHeaders){
59850             var h = this.getHeaderCellMeasure(colIndex);
59851             maxWidth = Math.max(maxWidth, h.scrollWidth);
59852         }
59853         var tb, index;
59854         if(this.cm.isLocked(colIndex)){
59855             tb = this.getLockedTable();
59856             index = colIndex;
59857         }else{
59858             tb = this.getBodyTable();
59859             index = colIndex - this.cm.getLockedCount();
59860         }
59861         if(tb && tb.rows){
59862             var rows = tb.rows;
59863             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59864             for(var i = 0; i < stopIndex; i++){
59865                 var cell = rows[i].childNodes[index].firstChild;
59866                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59867             }
59868         }
59869         return maxWidth + /*margin for error in IE*/ 5;
59870     },
59871     /**
59872      * Autofit a column to its content.
59873      * @param {Number} colIndex
59874      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59875      */
59876      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59877          if(this.cm.isHidden(colIndex)){
59878              return; // can't calc a hidden column
59879          }
59880         if(forceMinSize){
59881             var cid = this.cm.getColumnId(colIndex);
59882             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59883            if(this.grid.autoSizeHeaders){
59884                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59885            }
59886         }
59887         var newWidth = this.calcColumnWidth(colIndex);
59888         this.cm.setColumnWidth(colIndex,
59889             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59890         if(!suppressEvent){
59891             this.grid.fireEvent("columnresize", colIndex, newWidth);
59892         }
59893     },
59894
59895     /**
59896      * Autofits all columns to their content and then expands to fit any extra space in the grid
59897      */
59898      autoSizeColumns : function(){
59899         var cm = this.grid.colModel;
59900         var colCount = cm.getColumnCount();
59901         for(var i = 0; i < colCount; i++){
59902             this.autoSizeColumn(i, true, true);
59903         }
59904         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59905             this.fitColumns();
59906         }else{
59907             this.updateColumns();
59908             this.layout();
59909         }
59910     },
59911
59912     /**
59913      * Autofits all columns to the grid's width proportionate with their current size
59914      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59915      */
59916     fitColumns : function(reserveScrollSpace){
59917         var cm = this.grid.colModel;
59918         var colCount = cm.getColumnCount();
59919         var cols = [];
59920         var width = 0;
59921         var i, w;
59922         for (i = 0; i < colCount; i++){
59923             if(!cm.isHidden(i) && !cm.isFixed(i)){
59924                 w = cm.getColumnWidth(i);
59925                 cols.push(i);
59926                 cols.push(w);
59927                 width += w;
59928             }
59929         }
59930         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59931         if(reserveScrollSpace){
59932             avail -= 17;
59933         }
59934         var frac = (avail - cm.getTotalWidth())/width;
59935         while (cols.length){
59936             w = cols.pop();
59937             i = cols.pop();
59938             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59939         }
59940         this.updateColumns();
59941         this.layout();
59942     },
59943
59944     onRowSelect : function(rowIndex){
59945         var row = this.getRowComposite(rowIndex);
59946         row.addClass("x-grid-row-selected");
59947     },
59948
59949     onRowDeselect : function(rowIndex){
59950         var row = this.getRowComposite(rowIndex);
59951         row.removeClass("x-grid-row-selected");
59952     },
59953
59954     onCellSelect : function(row, col){
59955         var cell = this.getCell(row, col);
59956         if(cell){
59957             Roo.fly(cell).addClass("x-grid-cell-selected");
59958         }
59959     },
59960
59961     onCellDeselect : function(row, col){
59962         var cell = this.getCell(row, col);
59963         if(cell){
59964             Roo.fly(cell).removeClass("x-grid-cell-selected");
59965         }
59966     },
59967
59968     updateHeaderSortState : function(){
59969         
59970         // sort state can be single { field: xxx, direction : yyy}
59971         // or   { xxx=>ASC , yyy : DESC ..... }
59972         
59973         var mstate = {};
59974         if (!this.ds.multiSort) { 
59975             var state = this.ds.getSortState();
59976             if(!state){
59977                 return;
59978             }
59979             mstate[state.field] = state.direction;
59980             // FIXME... - this is not used here.. but might be elsewhere..
59981             this.sortState = state;
59982             
59983         } else {
59984             mstate = this.ds.sortToggle;
59985         }
59986         //remove existing sort classes..
59987         
59988         var sc = this.sortClasses;
59989         var hds = this.el.select(this.headerSelector).removeClass(sc);
59990         
59991         for(var f in mstate) {
59992         
59993             var sortColumn = this.cm.findColumnIndex(f);
59994             
59995             if(sortColumn != -1){
59996                 var sortDir = mstate[f];        
59997                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59998             }
59999         }
60000         
60001          
60002         
60003     },
60004
60005
60006     handleHeaderClick : function(g, index,e){
60007         
60008         Roo.log("header click");
60009         
60010         if (Roo.isTouch) {
60011             // touch events on header are handled by context
60012             this.handleHdCtx(g,index,e);
60013             return;
60014         }
60015         
60016         
60017         if(this.headersDisabled){
60018             return;
60019         }
60020         var dm = g.dataSource, cm = g.colModel;
60021         if(!cm.isSortable(index)){
60022             return;
60023         }
60024         g.stopEditing();
60025         
60026         if (dm.multiSort) {
60027             // update the sortOrder
60028             var so = [];
60029             for(var i = 0; i < cm.config.length; i++ ) {
60030                 
60031                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60032                     continue; // dont' bother, it's not in sort list or being set.
60033                 }
60034                 
60035                 so.push(cm.config[i].dataIndex);
60036             };
60037             dm.sortOrder = so;
60038         }
60039         
60040         
60041         dm.sort(cm.getDataIndex(index));
60042     },
60043
60044
60045     destroy : function(){
60046         if(this.colMenu){
60047             this.colMenu.removeAll();
60048             Roo.menu.MenuMgr.unregister(this.colMenu);
60049             this.colMenu.getEl().remove();
60050             delete this.colMenu;
60051         }
60052         if(this.hmenu){
60053             this.hmenu.removeAll();
60054             Roo.menu.MenuMgr.unregister(this.hmenu);
60055             this.hmenu.getEl().remove();
60056             delete this.hmenu;
60057         }
60058         if(this.grid.enableColumnMove){
60059             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60060             if(dds){
60061                 for(var dd in dds){
60062                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60063                         var elid = dds[dd].dragElId;
60064                         dds[dd].unreg();
60065                         Roo.get(elid).remove();
60066                     } else if(dds[dd].config.isTarget){
60067                         dds[dd].proxyTop.remove();
60068                         dds[dd].proxyBottom.remove();
60069                         dds[dd].unreg();
60070                     }
60071                     if(Roo.dd.DDM.locationCache[dd]){
60072                         delete Roo.dd.DDM.locationCache[dd];
60073                     }
60074                 }
60075                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60076             }
60077         }
60078         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60079         this.bind(null, null);
60080         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60081     },
60082
60083     handleLockChange : function(){
60084         this.refresh(true);
60085     },
60086
60087     onDenyColumnLock : function(){
60088
60089     },
60090
60091     onDenyColumnHide : function(){
60092
60093     },
60094
60095     handleHdMenuClick : function(item){
60096         var index = this.hdCtxIndex;
60097         var cm = this.cm, ds = this.ds;
60098         switch(item.id){
60099             case "asc":
60100                 ds.sort(cm.getDataIndex(index), "ASC");
60101                 break;
60102             case "desc":
60103                 ds.sort(cm.getDataIndex(index), "DESC");
60104                 break;
60105             case "lock":
60106                 var lc = cm.getLockedCount();
60107                 if(cm.getColumnCount(true) <= lc+1){
60108                     this.onDenyColumnLock();
60109                     return;
60110                 }
60111                 if(lc != index){
60112                     cm.setLocked(index, true, true);
60113                     cm.moveColumn(index, lc);
60114                     this.grid.fireEvent("columnmove", index, lc);
60115                 }else{
60116                     cm.setLocked(index, true);
60117                 }
60118             break;
60119             case "unlock":
60120                 var lc = cm.getLockedCount();
60121                 if((lc-1) != index){
60122                     cm.setLocked(index, false, true);
60123                     cm.moveColumn(index, lc-1);
60124                     this.grid.fireEvent("columnmove", index, lc-1);
60125                 }else{
60126                     cm.setLocked(index, false);
60127                 }
60128             break;
60129             case 'wider': // used to expand cols on touch..
60130             case 'narrow':
60131                 var cw = cm.getColumnWidth(index);
60132                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60133                 cw = Math.max(0, cw);
60134                 cw = Math.min(cw,4000);
60135                 cm.setColumnWidth(index, cw);
60136                 break;
60137                 
60138             default:
60139                 index = cm.getIndexById(item.id.substr(4));
60140                 if(index != -1){
60141                     if(item.checked && cm.getColumnCount(true) <= 1){
60142                         this.onDenyColumnHide();
60143                         return false;
60144                     }
60145                     cm.setHidden(index, item.checked);
60146                 }
60147         }
60148         return true;
60149     },
60150
60151     beforeColMenuShow : function(){
60152         var cm = this.cm,  colCount = cm.getColumnCount();
60153         this.colMenu.removeAll();
60154         
60155         var items = [];
60156         for(var i = 0; i < colCount; i++){
60157             items.push({
60158                 id: "col-"+cm.getColumnId(i),
60159                 text: cm.getColumnHeader(i),
60160                 checked: !cm.isHidden(i),
60161                 hideOnClick:false
60162             });
60163         }
60164         
60165         if (this.grid.sortColMenu) {
60166             items.sort(function(a,b) {
60167                 if (a.text == b.text) {
60168                     return 0;
60169                 }
60170                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60171             });
60172         }
60173         
60174         for(var i = 0; i < colCount; i++){
60175             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60176         }
60177     },
60178
60179     handleHdCtx : function(g, index, e){
60180         e.stopEvent();
60181         var hd = this.getHeaderCell(index);
60182         this.hdCtxIndex = index;
60183         var ms = this.hmenu.items, cm = this.cm;
60184         ms.get("asc").setDisabled(!cm.isSortable(index));
60185         ms.get("desc").setDisabled(!cm.isSortable(index));
60186         if(this.grid.enableColLock !== false){
60187             ms.get("lock").setDisabled(cm.isLocked(index));
60188             ms.get("unlock").setDisabled(!cm.isLocked(index));
60189         }
60190         this.hmenu.show(hd, "tl-bl");
60191     },
60192
60193     handleHdOver : function(e){
60194         var hd = this.findHeaderCell(e.getTarget());
60195         if(hd && !this.headersDisabled){
60196             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60197                this.fly(hd).addClass("x-grid-hd-over");
60198             }
60199         }
60200     },
60201
60202     handleHdOut : function(e){
60203         var hd = this.findHeaderCell(e.getTarget());
60204         if(hd){
60205             this.fly(hd).removeClass("x-grid-hd-over");
60206         }
60207     },
60208
60209     handleSplitDblClick : function(e, t){
60210         var i = this.getCellIndex(t);
60211         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60212             this.autoSizeColumn(i, true);
60213             this.layout();
60214         }
60215     },
60216
60217     render : function(){
60218
60219         var cm = this.cm;
60220         var colCount = cm.getColumnCount();
60221
60222         if(this.grid.monitorWindowResize === true){
60223             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60224         }
60225         var header = this.renderHeaders();
60226         var body = this.templates.body.apply({rows:""});
60227         var html = this.templates.master.apply({
60228             lockedBody: body,
60229             body: body,
60230             lockedHeader: header[0],
60231             header: header[1]
60232         });
60233
60234         //this.updateColumns();
60235
60236         this.grid.getGridEl().dom.innerHTML = html;
60237
60238         this.initElements();
60239         
60240         // a kludge to fix the random scolling effect in webkit
60241         this.el.on("scroll", function() {
60242             this.el.dom.scrollTop=0; // hopefully not recursive..
60243         },this);
60244
60245         this.scroller.on("scroll", this.handleScroll, this);
60246         this.lockedBody.on("mousewheel", this.handleWheel, this);
60247         this.mainBody.on("mousewheel", this.handleWheel, this);
60248
60249         this.mainHd.on("mouseover", this.handleHdOver, this);
60250         this.mainHd.on("mouseout", this.handleHdOut, this);
60251         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60252                 {delegate: "."+this.splitClass});
60253
60254         this.lockedHd.on("mouseover", this.handleHdOver, this);
60255         this.lockedHd.on("mouseout", this.handleHdOut, this);
60256         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60257                 {delegate: "."+this.splitClass});
60258
60259         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60260             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60261         }
60262
60263         this.updateSplitters();
60264
60265         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60266             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60267             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60268         }
60269
60270         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60271             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60272             this.hmenu.add(
60273                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60274                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60275             );
60276             if(this.grid.enableColLock !== false){
60277                 this.hmenu.add('-',
60278                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60279                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60280                 );
60281             }
60282             if (Roo.isTouch) {
60283                  this.hmenu.add('-',
60284                     {id:"wider", text: this.columnsWiderText},
60285                     {id:"narrow", text: this.columnsNarrowText }
60286                 );
60287                 
60288                  
60289             }
60290             
60291             if(this.grid.enableColumnHide !== false){
60292
60293                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60294                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60295                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60296
60297                 this.hmenu.add('-',
60298                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60299                 );
60300             }
60301             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60302
60303             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60304         }
60305
60306         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60307             this.dd = new Roo.grid.GridDragZone(this.grid, {
60308                 ddGroup : this.grid.ddGroup || 'GridDD'
60309             });
60310             
60311         }
60312
60313         /*
60314         for(var i = 0; i < colCount; i++){
60315             if(cm.isHidden(i)){
60316                 this.hideColumn(i);
60317             }
60318             if(cm.config[i].align){
60319                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60320                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60321             }
60322         }*/
60323         
60324         this.updateHeaderSortState();
60325
60326         this.beforeInitialResize();
60327         this.layout(true);
60328
60329         // two part rendering gives faster view to the user
60330         this.renderPhase2.defer(1, this);
60331     },
60332
60333     renderPhase2 : function(){
60334         // render the rows now
60335         this.refresh();
60336         if(this.grid.autoSizeColumns){
60337             this.autoSizeColumns();
60338         }
60339     },
60340
60341     beforeInitialResize : function(){
60342
60343     },
60344
60345     onColumnSplitterMoved : function(i, w){
60346         this.userResized = true;
60347         var cm = this.grid.colModel;
60348         cm.setColumnWidth(i, w, true);
60349         var cid = cm.getColumnId(i);
60350         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60351         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60352         this.updateSplitters();
60353         this.layout();
60354         this.grid.fireEvent("columnresize", i, w);
60355     },
60356
60357     syncRowHeights : function(startIndex, endIndex){
60358         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60359             startIndex = startIndex || 0;
60360             var mrows = this.getBodyTable().rows;
60361             var lrows = this.getLockedTable().rows;
60362             var len = mrows.length-1;
60363             endIndex = Math.min(endIndex || len, len);
60364             for(var i = startIndex; i <= endIndex; i++){
60365                 var m = mrows[i], l = lrows[i];
60366                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60367                 m.style.height = l.style.height = h + "px";
60368             }
60369         }
60370     },
60371
60372     layout : function(initialRender, is2ndPass)
60373     {
60374         var g = this.grid;
60375         var auto = g.autoHeight;
60376         var scrollOffset = 16;
60377         var c = g.getGridEl(), cm = this.cm,
60378                 expandCol = g.autoExpandColumn,
60379                 gv = this;
60380         //c.beginMeasure();
60381
60382         if(!c.dom.offsetWidth){ // display:none?
60383             if(initialRender){
60384                 this.lockedWrap.show();
60385                 this.mainWrap.show();
60386             }
60387             return;
60388         }
60389
60390         var hasLock = this.cm.isLocked(0);
60391
60392         var tbh = this.headerPanel.getHeight();
60393         var bbh = this.footerPanel.getHeight();
60394
60395         if(auto){
60396             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60397             var newHeight = ch + c.getBorderWidth("tb");
60398             if(g.maxHeight){
60399                 newHeight = Math.min(g.maxHeight, newHeight);
60400             }
60401             c.setHeight(newHeight);
60402         }
60403
60404         if(g.autoWidth){
60405             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60406         }
60407
60408         var s = this.scroller;
60409
60410         var csize = c.getSize(true);
60411
60412         this.el.setSize(csize.width, csize.height);
60413
60414         this.headerPanel.setWidth(csize.width);
60415         this.footerPanel.setWidth(csize.width);
60416
60417         var hdHeight = this.mainHd.getHeight();
60418         var vw = csize.width;
60419         var vh = csize.height - (tbh + bbh);
60420
60421         s.setSize(vw, vh);
60422
60423         var bt = this.getBodyTable();
60424         
60425         if(cm.getLockedCount() == cm.config.length){
60426             bt = this.getLockedTable();
60427         }
60428         
60429         var ltWidth = hasLock ?
60430                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60431
60432         var scrollHeight = bt.offsetHeight;
60433         var scrollWidth = ltWidth + bt.offsetWidth;
60434         var vscroll = false, hscroll = false;
60435
60436         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60437
60438         var lw = this.lockedWrap, mw = this.mainWrap;
60439         var lb = this.lockedBody, mb = this.mainBody;
60440
60441         setTimeout(function(){
60442             var t = s.dom.offsetTop;
60443             var w = s.dom.clientWidth,
60444                 h = s.dom.clientHeight;
60445
60446             lw.setTop(t);
60447             lw.setSize(ltWidth, h);
60448
60449             mw.setLeftTop(ltWidth, t);
60450             mw.setSize(w-ltWidth, h);
60451
60452             lb.setHeight(h-hdHeight);
60453             mb.setHeight(h-hdHeight);
60454
60455             if(is2ndPass !== true && !gv.userResized && expandCol){
60456                 // high speed resize without full column calculation
60457                 
60458                 var ci = cm.getIndexById(expandCol);
60459                 if (ci < 0) {
60460                     ci = cm.findColumnIndex(expandCol);
60461                 }
60462                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60463                 var expandId = cm.getColumnId(ci);
60464                 var  tw = cm.getTotalWidth(false);
60465                 var currentWidth = cm.getColumnWidth(ci);
60466                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60467                 if(currentWidth != cw){
60468                     cm.setColumnWidth(ci, cw, true);
60469                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60470                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60471                     gv.updateSplitters();
60472                     gv.layout(false, true);
60473                 }
60474             }
60475
60476             if(initialRender){
60477                 lw.show();
60478                 mw.show();
60479             }
60480             //c.endMeasure();
60481         }, 10);
60482     },
60483
60484     onWindowResize : function(){
60485         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60486             return;
60487         }
60488         this.layout();
60489     },
60490
60491     appendFooter : function(parentEl){
60492         return null;
60493     },
60494
60495     sortAscText : "Sort Ascending",
60496     sortDescText : "Sort Descending",
60497     lockText : "Lock Column",
60498     unlockText : "Unlock Column",
60499     columnsText : "Columns",
60500  
60501     columnsWiderText : "Wider",
60502     columnsNarrowText : "Thinner"
60503 });
60504
60505
60506 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60507     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60508     this.proxy.el.addClass('x-grid3-col-dd');
60509 };
60510
60511 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60512     handleMouseDown : function(e){
60513
60514     },
60515
60516     callHandleMouseDown : function(e){
60517         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60518     }
60519 });
60520 /*
60521  * Based on:
60522  * Ext JS Library 1.1.1
60523  * Copyright(c) 2006-2007, Ext JS, LLC.
60524  *
60525  * Originally Released Under LGPL - original licence link has changed is not relivant.
60526  *
60527  * Fork - LGPL
60528  * <script type="text/javascript">
60529  */
60530  /**
60531  * @extends Roo.dd.DDProxy
60532  * @class Roo.grid.SplitDragZone
60533  * Support for Column Header resizing
60534  * @constructor
60535  * @param {Object} config
60536  */
60537 // private
60538 // This is a support class used internally by the Grid components
60539 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60540     this.grid = grid;
60541     this.view = grid.getView();
60542     this.proxy = this.view.resizeProxy;
60543     Roo.grid.SplitDragZone.superclass.constructor.call(
60544         this,
60545         hd, // ID
60546         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60547         {  // CONFIG
60548             dragElId : Roo.id(this.proxy.dom),
60549             resizeFrame:false
60550         }
60551     );
60552     
60553     this.setHandleElId(Roo.id(hd));
60554     if (hd2 !== false) {
60555         this.setOuterHandleElId(Roo.id(hd2));
60556     }
60557     
60558     this.scroll = false;
60559 };
60560 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60561     fly: Roo.Element.fly,
60562
60563     b4StartDrag : function(x, y){
60564         this.view.headersDisabled = true;
60565         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60566                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60567         );
60568         this.proxy.setHeight(h);
60569         
60570         // for old system colWidth really stored the actual width?
60571         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60572         // which in reality did not work.. - it worked only for fixed sizes
60573         // for resizable we need to use actual sizes.
60574         var w = this.cm.getColumnWidth(this.cellIndex);
60575         if (!this.view.mainWrap) {
60576             // bootstrap.
60577             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60578         }
60579         
60580         
60581         
60582         // this was w-this.grid.minColumnWidth;
60583         // doesnt really make sense? - w = thie curren width or the rendered one?
60584         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60585         this.resetConstraints();
60586         this.setXConstraint(minw, 1000);
60587         this.setYConstraint(0, 0);
60588         this.minX = x - minw;
60589         this.maxX = x + 1000;
60590         this.startPos = x;
60591         if (!this.view.mainWrap) { // this is Bootstrap code..
60592             this.getDragEl().style.display='block';
60593         }
60594         
60595         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60596     },
60597
60598
60599     handleMouseDown : function(e){
60600         ev = Roo.EventObject.setEvent(e);
60601         var t = this.fly(ev.getTarget());
60602         if(t.hasClass("x-grid-split")){
60603             this.cellIndex = this.view.getCellIndex(t.dom);
60604             this.split = t.dom;
60605             this.cm = this.grid.colModel;
60606             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60607                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60608             }
60609         }
60610     },
60611
60612     endDrag : function(e){
60613         this.view.headersDisabled = false;
60614         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60615         var diff = endX - this.startPos;
60616         // 
60617         var w = this.cm.getColumnWidth(this.cellIndex);
60618         if (!this.view.mainWrap) {
60619             w = 0;
60620         }
60621         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60622     },
60623
60624     autoOffset : function(){
60625         this.setDelta(0,0);
60626     }
60627 });/*
60628  * Based on:
60629  * Ext JS Library 1.1.1
60630  * Copyright(c) 2006-2007, Ext JS, LLC.
60631  *
60632  * Originally Released Under LGPL - original licence link has changed is not relivant.
60633  *
60634  * Fork - LGPL
60635  * <script type="text/javascript">
60636  */
60637  
60638 // private
60639 // This is a support class used internally by the Grid components
60640 Roo.grid.GridDragZone = function(grid, config){
60641     this.view = grid.getView();
60642     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60643     if(this.view.lockedBody){
60644         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60645         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60646     }
60647     this.scroll = false;
60648     this.grid = grid;
60649     this.ddel = document.createElement('div');
60650     this.ddel.className = 'x-grid-dd-wrap';
60651 };
60652
60653 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60654     ddGroup : "GridDD",
60655
60656     getDragData : function(e){
60657         var t = Roo.lib.Event.getTarget(e);
60658         var rowIndex = this.view.findRowIndex(t);
60659         var sm = this.grid.selModel;
60660             
60661         //Roo.log(rowIndex);
60662         
60663         if (sm.getSelectedCell) {
60664             // cell selection..
60665             if (!sm.getSelectedCell()) {
60666                 return false;
60667             }
60668             if (rowIndex != sm.getSelectedCell()[0]) {
60669                 return false;
60670             }
60671         
60672         }
60673         if (sm.getSelections && sm.getSelections().length < 1) {
60674             return false;
60675         }
60676         
60677         
60678         // before it used to all dragging of unseleted... - now we dont do that.
60679         if(rowIndex !== false){
60680             
60681             // if editorgrid.. 
60682             
60683             
60684             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60685                
60686             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60687               //  
60688             //}
60689             if (e.hasModifier()){
60690                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60691             }
60692             
60693             Roo.log("getDragData");
60694             
60695             return {
60696                 grid: this.grid,
60697                 ddel: this.ddel,
60698                 rowIndex: rowIndex,
60699                 selections: sm.getSelections ? sm.getSelections() : (
60700                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60701             };
60702         }
60703         return false;
60704     },
60705     
60706     
60707     onInitDrag : function(e){
60708         var data = this.dragData;
60709         this.ddel.innerHTML = this.grid.getDragDropText();
60710         this.proxy.update(this.ddel);
60711         // fire start drag?
60712     },
60713
60714     afterRepair : function(){
60715         this.dragging = false;
60716     },
60717
60718     getRepairXY : function(e, data){
60719         return false;
60720     },
60721
60722     onEndDrag : function(data, e){
60723         // fire end drag?
60724     },
60725
60726     onValidDrop : function(dd, e, id){
60727         // fire drag drop?
60728         this.hideProxy();
60729     },
60730
60731     beforeInvalidDrop : function(e, id){
60732
60733     }
60734 });/*
60735  * Based on:
60736  * Ext JS Library 1.1.1
60737  * Copyright(c) 2006-2007, Ext JS, LLC.
60738  *
60739  * Originally Released Under LGPL - original licence link has changed is not relivant.
60740  *
60741  * Fork - LGPL
60742  * <script type="text/javascript">
60743  */
60744  
60745
60746 /**
60747  * @class Roo.grid.ColumnModel
60748  * @extends Roo.util.Observable
60749  * This is the default implementation of a ColumnModel used by the Grid. It defines
60750  * the columns in the grid.
60751  * <br>Usage:<br>
60752  <pre><code>
60753  var colModel = new Roo.grid.ColumnModel([
60754         {header: "Ticker", width: 60, sortable: true, locked: true},
60755         {header: "Company Name", width: 150, sortable: true},
60756         {header: "Market Cap.", width: 100, sortable: true},
60757         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60758         {header: "Employees", width: 100, sortable: true, resizable: false}
60759  ]);
60760  </code></pre>
60761  * <p>
60762  
60763  * The config options listed for this class are options which may appear in each
60764  * individual column definition.
60765  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60766  * @constructor
60767  * @param {Object} config An Array of column config objects. See this class's
60768  * config objects for details.
60769 */
60770 Roo.grid.ColumnModel = function(config){
60771         /**
60772      * The config passed into the constructor
60773      */
60774     this.config = []; //config;
60775     this.lookup = {};
60776
60777     // if no id, create one
60778     // if the column does not have a dataIndex mapping,
60779     // map it to the order it is in the config
60780     for(var i = 0, len = config.length; i < len; i++){
60781         this.addColumn(config[i]);
60782         
60783     }
60784
60785     /**
60786      * The width of columns which have no width specified (defaults to 100)
60787      * @type Number
60788      */
60789     this.defaultWidth = 100;
60790
60791     /**
60792      * Default sortable of columns which have no sortable specified (defaults to false)
60793      * @type Boolean
60794      */
60795     this.defaultSortable = false;
60796
60797     this.addEvents({
60798         /**
60799              * @event widthchange
60800              * Fires when the width of a column changes.
60801              * @param {ColumnModel} this
60802              * @param {Number} columnIndex The column index
60803              * @param {Number} newWidth The new width
60804              */
60805             "widthchange": true,
60806         /**
60807              * @event headerchange
60808              * Fires when the text of a header changes.
60809              * @param {ColumnModel} this
60810              * @param {Number} columnIndex The column index
60811              * @param {Number} newText The new header text
60812              */
60813             "headerchange": true,
60814         /**
60815              * @event hiddenchange
60816              * Fires when a column is hidden or "unhidden".
60817              * @param {ColumnModel} this
60818              * @param {Number} columnIndex The column index
60819              * @param {Boolean} hidden true if hidden, false otherwise
60820              */
60821             "hiddenchange": true,
60822             /**
60823          * @event columnmoved
60824          * Fires when a column is moved.
60825          * @param {ColumnModel} this
60826          * @param {Number} oldIndex
60827          * @param {Number} newIndex
60828          */
60829         "columnmoved" : true,
60830         /**
60831          * @event columlockchange
60832          * Fires when a column's locked state is changed
60833          * @param {ColumnModel} this
60834          * @param {Number} colIndex
60835          * @param {Boolean} locked true if locked
60836          */
60837         "columnlockchange" : true
60838     });
60839     Roo.grid.ColumnModel.superclass.constructor.call(this);
60840 };
60841 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60842     /**
60843      * @cfg {String} header The header text to display in the Grid view.
60844      */
60845         /**
60846      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60847      */
60848         /**
60849      * @cfg {String} smHeader Header at Bootsrap Small width
60850      */
60851         /**
60852      * @cfg {String} mdHeader Header at Bootsrap Medium width
60853      */
60854         /**
60855      * @cfg {String} lgHeader Header at Bootsrap Large width
60856      */
60857         /**
60858      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60859      */
60860     /**
60861      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60862      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60863      * specified, the column's index is used as an index into the Record's data Array.
60864      */
60865     /**
60866      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60867      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60868      */
60869     /**
60870      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60871      * Defaults to the value of the {@link #defaultSortable} property.
60872      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60873      */
60874     /**
60875      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60876      */
60877     /**
60878      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60879      */
60880     /**
60881      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60882      */
60883     /**
60884      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60885      */
60886     /**
60887      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60888      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60889      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60890      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60891      */
60892        /**
60893      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60894      */
60895     /**
60896      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60897      */
60898     /**
60899      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60900      */
60901     /**
60902      * @cfg {String} cursor (Optional)
60903      */
60904     /**
60905      * @cfg {String} tooltip (Optional)
60906      */
60907     /**
60908      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60909      */
60910     /**
60911      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60912      */
60913     /**
60914      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60915      */
60916     /**
60917      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60918      */
60919         /**
60920      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60921      */
60922     /**
60923      * Returns the id of the column at the specified index.
60924      * @param {Number} index The column index
60925      * @return {String} the id
60926      */
60927     getColumnId : function(index){
60928         return this.config[index].id;
60929     },
60930
60931     /**
60932      * Returns the column for a specified id.
60933      * @param {String} id The column id
60934      * @return {Object} the column
60935      */
60936     getColumnById : function(id){
60937         return this.lookup[id];
60938     },
60939
60940     
60941     /**
60942      * Returns the column Object for a specified dataIndex.
60943      * @param {String} dataIndex The column dataIndex
60944      * @return {Object|Boolean} the column or false if not found
60945      */
60946     getColumnByDataIndex: function(dataIndex){
60947         var index = this.findColumnIndex(dataIndex);
60948         return index > -1 ? this.config[index] : false;
60949     },
60950     
60951     /**
60952      * Returns the index for a specified column id.
60953      * @param {String} id The column id
60954      * @return {Number} the index, or -1 if not found
60955      */
60956     getIndexById : function(id){
60957         for(var i = 0, len = this.config.length; i < len; i++){
60958             if(this.config[i].id == id){
60959                 return i;
60960             }
60961         }
60962         return -1;
60963     },
60964     
60965     /**
60966      * Returns the index for a specified column dataIndex.
60967      * @param {String} dataIndex The column dataIndex
60968      * @return {Number} the index, or -1 if not found
60969      */
60970     
60971     findColumnIndex : function(dataIndex){
60972         for(var i = 0, len = this.config.length; i < len; i++){
60973             if(this.config[i].dataIndex == dataIndex){
60974                 return i;
60975             }
60976         }
60977         return -1;
60978     },
60979     
60980     
60981     moveColumn : function(oldIndex, newIndex){
60982         var c = this.config[oldIndex];
60983         this.config.splice(oldIndex, 1);
60984         this.config.splice(newIndex, 0, c);
60985         this.dataMap = null;
60986         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60987     },
60988
60989     isLocked : function(colIndex){
60990         return this.config[colIndex].locked === true;
60991     },
60992
60993     setLocked : function(colIndex, value, suppressEvent){
60994         if(this.isLocked(colIndex) == value){
60995             return;
60996         }
60997         this.config[colIndex].locked = value;
60998         if(!suppressEvent){
60999             this.fireEvent("columnlockchange", this, colIndex, value);
61000         }
61001     },
61002
61003     getTotalLockedWidth : function(){
61004         var totalWidth = 0;
61005         for(var i = 0; i < this.config.length; i++){
61006             if(this.isLocked(i) && !this.isHidden(i)){
61007                 this.totalWidth += this.getColumnWidth(i);
61008             }
61009         }
61010         return totalWidth;
61011     },
61012
61013     getLockedCount : function(){
61014         for(var i = 0, len = this.config.length; i < len; i++){
61015             if(!this.isLocked(i)){
61016                 return i;
61017             }
61018         }
61019         
61020         return this.config.length;
61021     },
61022
61023     /**
61024      * Returns the number of columns.
61025      * @return {Number}
61026      */
61027     getColumnCount : function(visibleOnly){
61028         if(visibleOnly === true){
61029             var c = 0;
61030             for(var i = 0, len = this.config.length; i < len; i++){
61031                 if(!this.isHidden(i)){
61032                     c++;
61033                 }
61034             }
61035             return c;
61036         }
61037         return this.config.length;
61038     },
61039
61040     /**
61041      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61042      * @param {Function} fn
61043      * @param {Object} scope (optional)
61044      * @return {Array} result
61045      */
61046     getColumnsBy : function(fn, scope){
61047         var r = [];
61048         for(var i = 0, len = this.config.length; i < len; i++){
61049             var c = this.config[i];
61050             if(fn.call(scope||this, c, i) === true){
61051                 r[r.length] = c;
61052             }
61053         }
61054         return r;
61055     },
61056
61057     /**
61058      * Returns true if the specified column is sortable.
61059      * @param {Number} col The column index
61060      * @return {Boolean}
61061      */
61062     isSortable : function(col){
61063         if(typeof this.config[col].sortable == "undefined"){
61064             return this.defaultSortable;
61065         }
61066         return this.config[col].sortable;
61067     },
61068
61069     /**
61070      * Returns the rendering (formatting) function defined for the column.
61071      * @param {Number} col The column index.
61072      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61073      */
61074     getRenderer : function(col){
61075         if(!this.config[col].renderer){
61076             return Roo.grid.ColumnModel.defaultRenderer;
61077         }
61078         return this.config[col].renderer;
61079     },
61080
61081     /**
61082      * Sets the rendering (formatting) function for a column.
61083      * @param {Number} col The column index
61084      * @param {Function} fn The function to use to process the cell's raw data
61085      * to return HTML markup for the grid view. The render function is called with
61086      * the following parameters:<ul>
61087      * <li>Data value.</li>
61088      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61089      * <li>css A CSS style string to apply to the table cell.</li>
61090      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61091      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61092      * <li>Row index</li>
61093      * <li>Column index</li>
61094      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61095      */
61096     setRenderer : function(col, fn){
61097         this.config[col].renderer = fn;
61098     },
61099
61100     /**
61101      * Returns the width for the specified column.
61102      * @param {Number} col The column index
61103      * @param (optional) {String} gridSize bootstrap width size.
61104      * @return {Number}
61105      */
61106     getColumnWidth : function(col, gridSize)
61107         {
61108                 var cfg = this.config[col];
61109                 
61110                 if (typeof(gridSize) == 'undefined') {
61111                         return cfg.width * 1 || this.defaultWidth;
61112                 }
61113                 if (gridSize === false) { // if we set it..
61114                         return cfg.width || false;
61115                 }
61116                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61117                 
61118                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61119                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61120                                 continue;
61121                         }
61122                         return cfg[ sizes[i] ];
61123                 }
61124                 return 1;
61125                 
61126     },
61127
61128     /**
61129      * Sets the width for a column.
61130      * @param {Number} col The column index
61131      * @param {Number} width The new width
61132      */
61133     setColumnWidth : function(col, width, suppressEvent){
61134         this.config[col].width = width;
61135         this.totalWidth = null;
61136         if(!suppressEvent){
61137              this.fireEvent("widthchange", this, col, width);
61138         }
61139     },
61140
61141     /**
61142      * Returns the total width of all columns.
61143      * @param {Boolean} includeHidden True to include hidden column widths
61144      * @return {Number}
61145      */
61146     getTotalWidth : function(includeHidden){
61147         if(!this.totalWidth){
61148             this.totalWidth = 0;
61149             for(var i = 0, len = this.config.length; i < len; i++){
61150                 if(includeHidden || !this.isHidden(i)){
61151                     this.totalWidth += this.getColumnWidth(i);
61152                 }
61153             }
61154         }
61155         return this.totalWidth;
61156     },
61157
61158     /**
61159      * Returns the header for the specified column.
61160      * @param {Number} col The column index
61161      * @return {String}
61162      */
61163     getColumnHeader : function(col){
61164         return this.config[col].header;
61165     },
61166
61167     /**
61168      * Sets the header for a column.
61169      * @param {Number} col The column index
61170      * @param {String} header The new header
61171      */
61172     setColumnHeader : function(col, header){
61173         this.config[col].header = header;
61174         this.fireEvent("headerchange", this, col, header);
61175     },
61176
61177     /**
61178      * Returns the tooltip for the specified column.
61179      * @param {Number} col The column index
61180      * @return {String}
61181      */
61182     getColumnTooltip : function(col){
61183             return this.config[col].tooltip;
61184     },
61185     /**
61186      * Sets the tooltip for a column.
61187      * @param {Number} col The column index
61188      * @param {String} tooltip The new tooltip
61189      */
61190     setColumnTooltip : function(col, tooltip){
61191             this.config[col].tooltip = tooltip;
61192     },
61193
61194     /**
61195      * Returns the dataIndex for the specified column.
61196      * @param {Number} col The column index
61197      * @return {Number}
61198      */
61199     getDataIndex : function(col){
61200         return this.config[col].dataIndex;
61201     },
61202
61203     /**
61204      * Sets the dataIndex for a column.
61205      * @param {Number} col The column index
61206      * @param {Number} dataIndex The new dataIndex
61207      */
61208     setDataIndex : function(col, dataIndex){
61209         this.config[col].dataIndex = dataIndex;
61210     },
61211
61212     
61213     
61214     /**
61215      * Returns true if the cell is editable.
61216      * @param {Number} colIndex The column index
61217      * @param {Number} rowIndex The row index - this is nto actually used..?
61218      * @return {Boolean}
61219      */
61220     isCellEditable : function(colIndex, rowIndex){
61221         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61222     },
61223
61224     /**
61225      * Returns the editor defined for the cell/column.
61226      * return false or null to disable editing.
61227      * @param {Number} colIndex The column index
61228      * @param {Number} rowIndex The row index
61229      * @return {Object}
61230      */
61231     getCellEditor : function(colIndex, rowIndex){
61232         return this.config[colIndex].editor;
61233     },
61234
61235     /**
61236      * Sets if a column is editable.
61237      * @param {Number} col The column index
61238      * @param {Boolean} editable True if the column is editable
61239      */
61240     setEditable : function(col, editable){
61241         this.config[col].editable = editable;
61242     },
61243
61244
61245     /**
61246      * Returns true if the column is hidden.
61247      * @param {Number} colIndex The column index
61248      * @return {Boolean}
61249      */
61250     isHidden : function(colIndex){
61251         return this.config[colIndex].hidden;
61252     },
61253
61254
61255     /**
61256      * Returns true if the column width cannot be changed
61257      */
61258     isFixed : function(colIndex){
61259         return this.config[colIndex].fixed;
61260     },
61261
61262     /**
61263      * Returns true if the column can be resized
61264      * @return {Boolean}
61265      */
61266     isResizable : function(colIndex){
61267         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61268     },
61269     /**
61270      * Sets if a column is hidden.
61271      * @param {Number} colIndex The column index
61272      * @param {Boolean} hidden True if the column is hidden
61273      */
61274     setHidden : function(colIndex, hidden){
61275         this.config[colIndex].hidden = hidden;
61276         this.totalWidth = null;
61277         this.fireEvent("hiddenchange", this, colIndex, hidden);
61278     },
61279
61280     /**
61281      * Sets the editor for a column.
61282      * @param {Number} col The column index
61283      * @param {Object} editor The editor object
61284      */
61285     setEditor : function(col, editor){
61286         this.config[col].editor = editor;
61287     },
61288     /**
61289      * Add a column (experimental...) - defaults to adding to the end..
61290      * @param {Object} config 
61291     */
61292     addColumn : function(c)
61293     {
61294     
61295         var i = this.config.length;
61296         this.config[i] = c;
61297         
61298         if(typeof c.dataIndex == "undefined"){
61299             c.dataIndex = i;
61300         }
61301         if(typeof c.renderer == "string"){
61302             c.renderer = Roo.util.Format[c.renderer];
61303         }
61304         if(typeof c.id == "undefined"){
61305             c.id = Roo.id();
61306         }
61307         if(c.editor && c.editor.xtype){
61308             c.editor  = Roo.factory(c.editor, Roo.grid);
61309         }
61310         if(c.editor && c.editor.isFormField){
61311             c.editor = new Roo.grid.GridEditor(c.editor);
61312         }
61313         this.lookup[c.id] = c;
61314     }
61315     
61316 });
61317
61318 Roo.grid.ColumnModel.defaultRenderer = function(value)
61319 {
61320     if(typeof value == "object") {
61321         return value;
61322     }
61323         if(typeof value == "string" && value.length < 1){
61324             return "&#160;";
61325         }
61326     
61327         return String.format("{0}", value);
61328 };
61329
61330 // Alias for backwards compatibility
61331 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61332 /*
61333  * Based on:
61334  * Ext JS Library 1.1.1
61335  * Copyright(c) 2006-2007, Ext JS, LLC.
61336  *
61337  * Originally Released Under LGPL - original licence link has changed is not relivant.
61338  *
61339  * Fork - LGPL
61340  * <script type="text/javascript">
61341  */
61342
61343 /**
61344  * @class Roo.grid.AbstractSelectionModel
61345  * @extends Roo.util.Observable
61346  * @abstract
61347  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61348  * implemented by descendant classes.  This class should not be directly instantiated.
61349  * @constructor
61350  */
61351 Roo.grid.AbstractSelectionModel = function(){
61352     this.locked = false;
61353     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61354 };
61355
61356 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61357     /** @ignore Called by the grid automatically. Do not call directly. */
61358     init : function(grid){
61359         this.grid = grid;
61360         this.initEvents();
61361     },
61362
61363     /**
61364      * Locks the selections.
61365      */
61366     lock : function(){
61367         this.locked = true;
61368     },
61369
61370     /**
61371      * Unlocks the selections.
61372      */
61373     unlock : function(){
61374         this.locked = false;
61375     },
61376
61377     /**
61378      * Returns true if the selections are locked.
61379      * @return {Boolean}
61380      */
61381     isLocked : function(){
61382         return this.locked;
61383     }
61384 });/*
61385  * Based on:
61386  * Ext JS Library 1.1.1
61387  * Copyright(c) 2006-2007, Ext JS, LLC.
61388  *
61389  * Originally Released Under LGPL - original licence link has changed is not relivant.
61390  *
61391  * Fork - LGPL
61392  * <script type="text/javascript">
61393  */
61394 /**
61395  * @extends Roo.grid.AbstractSelectionModel
61396  * @class Roo.grid.RowSelectionModel
61397  * The default SelectionModel used by {@link Roo.grid.Grid}.
61398  * It supports multiple selections and keyboard selection/navigation. 
61399  * @constructor
61400  * @param {Object} config
61401  */
61402 Roo.grid.RowSelectionModel = function(config){
61403     Roo.apply(this, config);
61404     this.selections = new Roo.util.MixedCollection(false, function(o){
61405         return o.id;
61406     });
61407
61408     this.last = false;
61409     this.lastActive = false;
61410
61411     this.addEvents({
61412         /**
61413         * @event selectionchange
61414         * Fires when the selection changes
61415         * @param {SelectionModel} this
61416         */
61417        "selectionchange" : true,
61418        /**
61419         * @event afterselectionchange
61420         * Fires after the selection changes (eg. by key press or clicking)
61421         * @param {SelectionModel} this
61422         */
61423        "afterselectionchange" : true,
61424        /**
61425         * @event beforerowselect
61426         * Fires when a row is selected being selected, return false to cancel.
61427         * @param {SelectionModel} this
61428         * @param {Number} rowIndex The selected index
61429         * @param {Boolean} keepExisting False if other selections will be cleared
61430         */
61431        "beforerowselect" : true,
61432        /**
61433         * @event rowselect
61434         * Fires when a row is selected.
61435         * @param {SelectionModel} this
61436         * @param {Number} rowIndex The selected index
61437         * @param {Roo.data.Record} r The record
61438         */
61439        "rowselect" : true,
61440        /**
61441         * @event rowdeselect
61442         * Fires when a row is deselected.
61443         * @param {SelectionModel} this
61444         * @param {Number} rowIndex The selected index
61445         */
61446         "rowdeselect" : true
61447     });
61448     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61449     this.locked = false;
61450 };
61451
61452 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61453     /**
61454      * @cfg {Boolean} singleSelect
61455      * True to allow selection of only one row at a time (defaults to false)
61456      */
61457     singleSelect : false,
61458
61459     // private
61460     initEvents : function(){
61461
61462         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61463             this.grid.on("mousedown", this.handleMouseDown, this);
61464         }else{ // allow click to work like normal
61465             this.grid.on("rowclick", this.handleDragableRowClick, this);
61466         }
61467         // bootstrap does not have a view..
61468         var view = this.grid.view ? this.grid.view : this.grid;
61469         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61470             "up" : function(e){
61471                 if(!e.shiftKey){
61472                     this.selectPrevious(e.shiftKey);
61473                 }else if(this.last !== false && this.lastActive !== false){
61474                     var last = this.last;
61475                     this.selectRange(this.last,  this.lastActive-1);
61476                     view.focusRow(this.lastActive);
61477                     if(last !== false){
61478                         this.last = last;
61479                     }
61480                 }else{
61481                     this.selectFirstRow();
61482                 }
61483                 this.fireEvent("afterselectionchange", this);
61484             },
61485             "down" : function(e){
61486                 if(!e.shiftKey){
61487                     this.selectNext(e.shiftKey);
61488                 }else if(this.last !== false && this.lastActive !== false){
61489                     var last = this.last;
61490                     this.selectRange(this.last,  this.lastActive+1);
61491                     view.focusRow(this.lastActive);
61492                     if(last !== false){
61493                         this.last = last;
61494                     }
61495                 }else{
61496                     this.selectFirstRow();
61497                 }
61498                 this.fireEvent("afterselectionchange", this);
61499             },
61500             scope: this
61501         });
61502
61503          
61504         view.on("refresh", this.onRefresh, this);
61505         view.on("rowupdated", this.onRowUpdated, this);
61506         view.on("rowremoved", this.onRemove, this);
61507     },
61508
61509     // private
61510     onRefresh : function(){
61511         var ds = this.grid.ds, i, v = this.grid.view;
61512         var s = this.selections;
61513         s.each(function(r){
61514             if((i = ds.indexOfId(r.id)) != -1){
61515                 v.onRowSelect(i);
61516                 s.add(ds.getAt(i)); // updating the selection relate data
61517             }else{
61518                 s.remove(r);
61519             }
61520         });
61521     },
61522
61523     // private
61524     onRemove : function(v, index, r){
61525         this.selections.remove(r);
61526     },
61527
61528     // private
61529     onRowUpdated : function(v, index, r){
61530         if(this.isSelected(r)){
61531             v.onRowSelect(index);
61532         }
61533     },
61534
61535     /**
61536      * Select records.
61537      * @param {Array} records The records to select
61538      * @param {Boolean} keepExisting (optional) True to keep existing selections
61539      */
61540     selectRecords : function(records, keepExisting){
61541         if(!keepExisting){
61542             this.clearSelections();
61543         }
61544         var ds = this.grid.ds;
61545         for(var i = 0, len = records.length; i < len; i++){
61546             this.selectRow(ds.indexOf(records[i]), true);
61547         }
61548     },
61549
61550     /**
61551      * Gets the number of selected rows.
61552      * @return {Number}
61553      */
61554     getCount : function(){
61555         return this.selections.length;
61556     },
61557
61558     /**
61559      * Selects the first row in the grid.
61560      */
61561     selectFirstRow : function(){
61562         this.selectRow(0);
61563     },
61564
61565     /**
61566      * Select the last row.
61567      * @param {Boolean} keepExisting (optional) True to keep existing selections
61568      */
61569     selectLastRow : function(keepExisting){
61570         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61571     },
61572
61573     /**
61574      * Selects the row immediately following the last selected row.
61575      * @param {Boolean} keepExisting (optional) True to keep existing selections
61576      */
61577     selectNext : function(keepExisting){
61578         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61579             this.selectRow(this.last+1, keepExisting);
61580             var view = this.grid.view ? this.grid.view : this.grid;
61581             view.focusRow(this.last);
61582         }
61583     },
61584
61585     /**
61586      * Selects the row that precedes the last selected row.
61587      * @param {Boolean} keepExisting (optional) True to keep existing selections
61588      */
61589     selectPrevious : function(keepExisting){
61590         if(this.last){
61591             this.selectRow(this.last-1, keepExisting);
61592             var view = this.grid.view ? this.grid.view : this.grid;
61593             view.focusRow(this.last);
61594         }
61595     },
61596
61597     /**
61598      * Returns the selected records
61599      * @return {Array} Array of selected records
61600      */
61601     getSelections : function(){
61602         return [].concat(this.selections.items);
61603     },
61604
61605     /**
61606      * Returns the first selected record.
61607      * @return {Record}
61608      */
61609     getSelected : function(){
61610         return this.selections.itemAt(0);
61611     },
61612
61613
61614     /**
61615      * Clears all selections.
61616      */
61617     clearSelections : function(fast){
61618         if(this.locked) {
61619             return;
61620         }
61621         if(fast !== true){
61622             var ds = this.grid.ds;
61623             var s = this.selections;
61624             s.each(function(r){
61625                 this.deselectRow(ds.indexOfId(r.id));
61626             }, this);
61627             s.clear();
61628         }else{
61629             this.selections.clear();
61630         }
61631         this.last = false;
61632     },
61633
61634
61635     /**
61636      * Selects all rows.
61637      */
61638     selectAll : function(){
61639         if(this.locked) {
61640             return;
61641         }
61642         this.selections.clear();
61643         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61644             this.selectRow(i, true);
61645         }
61646     },
61647
61648     /**
61649      * Returns True if there is a selection.
61650      * @return {Boolean}
61651      */
61652     hasSelection : function(){
61653         return this.selections.length > 0;
61654     },
61655
61656     /**
61657      * Returns True if the specified row is selected.
61658      * @param {Number/Record} record The record or index of the record to check
61659      * @return {Boolean}
61660      */
61661     isSelected : function(index){
61662         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61663         return (r && this.selections.key(r.id) ? true : false);
61664     },
61665
61666     /**
61667      * Returns True if the specified record id is selected.
61668      * @param {String} id The id of record to check
61669      * @return {Boolean}
61670      */
61671     isIdSelected : function(id){
61672         return (this.selections.key(id) ? true : false);
61673     },
61674
61675     // private
61676     handleMouseDown : function(e, t)
61677     {
61678         var view = this.grid.view ? this.grid.view : this.grid;
61679         var rowIndex;
61680         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61681             return;
61682         };
61683         if(e.shiftKey && this.last !== false){
61684             var last = this.last;
61685             this.selectRange(last, rowIndex, e.ctrlKey);
61686             this.last = last; // reset the last
61687             view.focusRow(rowIndex);
61688         }else{
61689             var isSelected = this.isSelected(rowIndex);
61690             if(e.button !== 0 && isSelected){
61691                 view.focusRow(rowIndex);
61692             }else if(e.ctrlKey && isSelected){
61693                 this.deselectRow(rowIndex);
61694             }else if(!isSelected){
61695                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61696                 view.focusRow(rowIndex);
61697             }
61698         }
61699         this.fireEvent("afterselectionchange", this);
61700     },
61701     // private
61702     handleDragableRowClick :  function(grid, rowIndex, e) 
61703     {
61704         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61705             this.selectRow(rowIndex, false);
61706             var view = this.grid.view ? this.grid.view : this.grid;
61707             view.focusRow(rowIndex);
61708              this.fireEvent("afterselectionchange", this);
61709         }
61710     },
61711     
61712     /**
61713      * Selects multiple rows.
61714      * @param {Array} rows Array of the indexes of the row to select
61715      * @param {Boolean} keepExisting (optional) True to keep existing selections
61716      */
61717     selectRows : function(rows, keepExisting){
61718         if(!keepExisting){
61719             this.clearSelections();
61720         }
61721         for(var i = 0, len = rows.length; i < len; i++){
61722             this.selectRow(rows[i], true);
61723         }
61724     },
61725
61726     /**
61727      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61728      * @param {Number} startRow The index of the first row in the range
61729      * @param {Number} endRow The index of the last row in the range
61730      * @param {Boolean} keepExisting (optional) True to retain existing selections
61731      */
61732     selectRange : function(startRow, endRow, keepExisting){
61733         if(this.locked) {
61734             return;
61735         }
61736         if(!keepExisting){
61737             this.clearSelections();
61738         }
61739         if(startRow <= endRow){
61740             for(var i = startRow; i <= endRow; i++){
61741                 this.selectRow(i, true);
61742             }
61743         }else{
61744             for(var i = startRow; i >= endRow; i--){
61745                 this.selectRow(i, true);
61746             }
61747         }
61748     },
61749
61750     /**
61751      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61752      * @param {Number} startRow The index of the first row in the range
61753      * @param {Number} endRow The index of the last row in the range
61754      */
61755     deselectRange : function(startRow, endRow, preventViewNotify){
61756         if(this.locked) {
61757             return;
61758         }
61759         for(var i = startRow; i <= endRow; i++){
61760             this.deselectRow(i, preventViewNotify);
61761         }
61762     },
61763
61764     /**
61765      * Selects a row.
61766      * @param {Number} row The index of the row to select
61767      * @param {Boolean} keepExisting (optional) True to keep existing selections
61768      */
61769     selectRow : function(index, keepExisting, preventViewNotify){
61770         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61771             return;
61772         }
61773         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61774             if(!keepExisting || this.singleSelect){
61775                 this.clearSelections();
61776             }
61777             var r = this.grid.ds.getAt(index);
61778             this.selections.add(r);
61779             this.last = this.lastActive = index;
61780             if(!preventViewNotify){
61781                 var view = this.grid.view ? this.grid.view : this.grid;
61782                 view.onRowSelect(index);
61783             }
61784             this.fireEvent("rowselect", this, index, r);
61785             this.fireEvent("selectionchange", this);
61786         }
61787     },
61788
61789     /**
61790      * Deselects a row.
61791      * @param {Number} row The index of the row to deselect
61792      */
61793     deselectRow : function(index, preventViewNotify){
61794         if(this.locked) {
61795             return;
61796         }
61797         if(this.last == index){
61798             this.last = false;
61799         }
61800         if(this.lastActive == index){
61801             this.lastActive = false;
61802         }
61803         var r = this.grid.ds.getAt(index);
61804         this.selections.remove(r);
61805         if(!preventViewNotify){
61806             var view = this.grid.view ? this.grid.view : this.grid;
61807             view.onRowDeselect(index);
61808         }
61809         this.fireEvent("rowdeselect", this, index);
61810         this.fireEvent("selectionchange", this);
61811     },
61812
61813     // private
61814     restoreLast : function(){
61815         if(this._last){
61816             this.last = this._last;
61817         }
61818     },
61819
61820     // private
61821     acceptsNav : function(row, col, cm){
61822         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61823     },
61824
61825     // private
61826     onEditorKey : function(field, e){
61827         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61828         if(k == e.TAB){
61829             e.stopEvent();
61830             ed.completeEdit();
61831             if(e.shiftKey){
61832                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61833             }else{
61834                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61835             }
61836         }else if(k == e.ENTER && !e.ctrlKey){
61837             e.stopEvent();
61838             ed.completeEdit();
61839             if(e.shiftKey){
61840                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61841             }else{
61842                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61843             }
61844         }else if(k == e.ESC){
61845             ed.cancelEdit();
61846         }
61847         if(newCell){
61848             g.startEditing(newCell[0], newCell[1]);
61849         }
61850     }
61851 });/*
61852  * Based on:
61853  * Ext JS Library 1.1.1
61854  * Copyright(c) 2006-2007, Ext JS, LLC.
61855  *
61856  * Originally Released Under LGPL - original licence link has changed is not relivant.
61857  *
61858  * Fork - LGPL
61859  * <script type="text/javascript">
61860  */
61861 /**
61862  * @class Roo.grid.CellSelectionModel
61863  * @extends Roo.grid.AbstractSelectionModel
61864  * This class provides the basic implementation for cell selection in a grid.
61865  * @constructor
61866  * @param {Object} config The object containing the configuration of this model.
61867  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61868  */
61869 Roo.grid.CellSelectionModel = function(config){
61870     Roo.apply(this, config);
61871
61872     this.selection = null;
61873
61874     this.addEvents({
61875         /**
61876              * @event beforerowselect
61877              * Fires before a cell is selected.
61878              * @param {SelectionModel} this
61879              * @param {Number} rowIndex The selected row index
61880              * @param {Number} colIndex The selected cell index
61881              */
61882             "beforecellselect" : true,
61883         /**
61884              * @event cellselect
61885              * Fires when a cell is selected.
61886              * @param {SelectionModel} this
61887              * @param {Number} rowIndex The selected row index
61888              * @param {Number} colIndex The selected cell index
61889              */
61890             "cellselect" : true,
61891         /**
61892              * @event selectionchange
61893              * Fires when the active selection changes.
61894              * @param {SelectionModel} this
61895              * @param {Object} selection null for no selection or an object (o) with two properties
61896                 <ul>
61897                 <li>o.record: the record object for the row the selection is in</li>
61898                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61899                 </ul>
61900              */
61901             "selectionchange" : true,
61902         /**
61903              * @event tabend
61904              * Fires when the tab (or enter) was pressed on the last editable cell
61905              * You can use this to trigger add new row.
61906              * @param {SelectionModel} this
61907              */
61908             "tabend" : true,
61909          /**
61910              * @event beforeeditnext
61911              * Fires before the next editable sell is made active
61912              * You can use this to skip to another cell or fire the tabend
61913              *    if you set cell to false
61914              * @param {Object} eventdata object : { cell : [ row, col ] } 
61915              */
61916             "beforeeditnext" : true
61917     });
61918     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61919 };
61920
61921 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61922     
61923     enter_is_tab: false,
61924
61925     /** @ignore */
61926     initEvents : function(){
61927         this.grid.on("mousedown", this.handleMouseDown, this);
61928         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61929         var view = this.grid.view;
61930         view.on("refresh", this.onViewChange, this);
61931         view.on("rowupdated", this.onRowUpdated, this);
61932         view.on("beforerowremoved", this.clearSelections, this);
61933         view.on("beforerowsinserted", this.clearSelections, this);
61934         if(this.grid.isEditor){
61935             this.grid.on("beforeedit", this.beforeEdit,  this);
61936         }
61937     },
61938
61939         //private
61940     beforeEdit : function(e){
61941         this.select(e.row, e.column, false, true, e.record);
61942     },
61943
61944         //private
61945     onRowUpdated : function(v, index, r){
61946         if(this.selection && this.selection.record == r){
61947             v.onCellSelect(index, this.selection.cell[1]);
61948         }
61949     },
61950
61951         //private
61952     onViewChange : function(){
61953         this.clearSelections(true);
61954     },
61955
61956         /**
61957          * Returns the currently selected cell,.
61958          * @return {Array} The selected cell (row, column) or null if none selected.
61959          */
61960     getSelectedCell : function(){
61961         return this.selection ? this.selection.cell : null;
61962     },
61963
61964     /**
61965      * Clears all selections.
61966      * @param {Boolean} true to prevent the gridview from being notified about the change.
61967      */
61968     clearSelections : function(preventNotify){
61969         var s = this.selection;
61970         if(s){
61971             if(preventNotify !== true){
61972                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61973             }
61974             this.selection = null;
61975             this.fireEvent("selectionchange", this, null);
61976         }
61977     },
61978
61979     /**
61980      * Returns true if there is a selection.
61981      * @return {Boolean}
61982      */
61983     hasSelection : function(){
61984         return this.selection ? true : false;
61985     },
61986
61987     /** @ignore */
61988     handleMouseDown : function(e, t){
61989         var v = this.grid.getView();
61990         if(this.isLocked()){
61991             return;
61992         };
61993         var row = v.findRowIndex(t);
61994         var cell = v.findCellIndex(t);
61995         if(row !== false && cell !== false){
61996             this.select(row, cell);
61997         }
61998     },
61999
62000     /**
62001      * Selects a cell.
62002      * @param {Number} rowIndex
62003      * @param {Number} collIndex
62004      */
62005     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62006         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62007             this.clearSelections();
62008             r = r || this.grid.dataSource.getAt(rowIndex);
62009             this.selection = {
62010                 record : r,
62011                 cell : [rowIndex, colIndex]
62012             };
62013             if(!preventViewNotify){
62014                 var v = this.grid.getView();
62015                 v.onCellSelect(rowIndex, colIndex);
62016                 if(preventFocus !== true){
62017                     v.focusCell(rowIndex, colIndex);
62018                 }
62019             }
62020             this.fireEvent("cellselect", this, rowIndex, colIndex);
62021             this.fireEvent("selectionchange", this, this.selection);
62022         }
62023     },
62024
62025         //private
62026     isSelectable : function(rowIndex, colIndex, cm){
62027         return !cm.isHidden(colIndex);
62028     },
62029
62030     /** @ignore */
62031     handleKeyDown : function(e){
62032         //Roo.log('Cell Sel Model handleKeyDown');
62033         if(!e.isNavKeyPress()){
62034             return;
62035         }
62036         var g = this.grid, s = this.selection;
62037         if(!s){
62038             e.stopEvent();
62039             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62040             if(cell){
62041                 this.select(cell[0], cell[1]);
62042             }
62043             return;
62044         }
62045         var sm = this;
62046         var walk = function(row, col, step){
62047             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62048         };
62049         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62050         var newCell;
62051
62052       
62053
62054         switch(k){
62055             case e.TAB:
62056                 // handled by onEditorKey
62057                 if (g.isEditor && g.editing) {
62058                     return;
62059                 }
62060                 if(e.shiftKey) {
62061                     newCell = walk(r, c-1, -1);
62062                 } else {
62063                     newCell = walk(r, c+1, 1);
62064                 }
62065                 break;
62066             
62067             case e.DOWN:
62068                newCell = walk(r+1, c, 1);
62069                 break;
62070             
62071             case e.UP:
62072                 newCell = walk(r-1, c, -1);
62073                 break;
62074             
62075             case e.RIGHT:
62076                 newCell = walk(r, c+1, 1);
62077                 break;
62078             
62079             case e.LEFT:
62080                 newCell = walk(r, c-1, -1);
62081                 break;
62082             
62083             case e.ENTER:
62084                 
62085                 if(g.isEditor && !g.editing){
62086                    g.startEditing(r, c);
62087                    e.stopEvent();
62088                    return;
62089                 }
62090                 
62091                 
62092              break;
62093         };
62094         if(newCell){
62095             this.select(newCell[0], newCell[1]);
62096             e.stopEvent();
62097             
62098         }
62099     },
62100
62101     acceptsNav : function(row, col, cm){
62102         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62103     },
62104     /**
62105      * Selects a cell.
62106      * @param {Number} field (not used) - as it's normally used as a listener
62107      * @param {Number} e - event - fake it by using
62108      *
62109      * var e = Roo.EventObjectImpl.prototype;
62110      * e.keyCode = e.TAB
62111      *
62112      * 
62113      */
62114     onEditorKey : function(field, e){
62115         
62116         var k = e.getKey(),
62117             newCell,
62118             g = this.grid,
62119             ed = g.activeEditor,
62120             forward = false;
62121         ///Roo.log('onEditorKey' + k);
62122         
62123         
62124         if (this.enter_is_tab && k == e.ENTER) {
62125             k = e.TAB;
62126         }
62127         
62128         if(k == e.TAB){
62129             if(e.shiftKey){
62130                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62131             }else{
62132                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62133                 forward = true;
62134             }
62135             
62136             e.stopEvent();
62137             
62138         } else if(k == e.ENTER &&  !e.ctrlKey){
62139             ed.completeEdit();
62140             e.stopEvent();
62141             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62142         
62143                 } else if(k == e.ESC){
62144             ed.cancelEdit();
62145         }
62146                 
62147         if (newCell) {
62148             var ecall = { cell : newCell, forward : forward };
62149             this.fireEvent('beforeeditnext', ecall );
62150             newCell = ecall.cell;
62151                         forward = ecall.forward;
62152         }
62153                 
62154         if(newCell){
62155             //Roo.log('next cell after edit');
62156             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62157         } else if (forward) {
62158             // tabbed past last
62159             this.fireEvent.defer(100, this, ['tabend',this]);
62160         }
62161     }
62162 });/*
62163  * Based on:
62164  * Ext JS Library 1.1.1
62165  * Copyright(c) 2006-2007, Ext JS, LLC.
62166  *
62167  * Originally Released Under LGPL - original licence link has changed is not relivant.
62168  *
62169  * Fork - LGPL
62170  * <script type="text/javascript">
62171  */
62172  
62173 /**
62174  * @class Roo.grid.EditorGrid
62175  * @extends Roo.grid.Grid
62176  * Class for creating and editable grid.
62177  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62178  * The container MUST have some type of size defined for the grid to fill. The container will be 
62179  * automatically set to position relative if it isn't already.
62180  * @param {Object} dataSource The data model to bind to
62181  * @param {Object} colModel The column model with info about this grid's columns
62182  */
62183 Roo.grid.EditorGrid = function(container, config){
62184     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62185     this.getGridEl().addClass("xedit-grid");
62186
62187     if(!this.selModel){
62188         this.selModel = new Roo.grid.CellSelectionModel();
62189     }
62190
62191     this.activeEditor = null;
62192
62193         this.addEvents({
62194             /**
62195              * @event beforeedit
62196              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62197              * <ul style="padding:5px;padding-left:16px;">
62198              * <li>grid - This grid</li>
62199              * <li>record - The record being edited</li>
62200              * <li>field - The field name being edited</li>
62201              * <li>value - The value for the field being edited.</li>
62202              * <li>row - The grid row index</li>
62203              * <li>column - The grid column index</li>
62204              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62205              * </ul>
62206              * @param {Object} e An edit event (see above for description)
62207              */
62208             "beforeedit" : true,
62209             /**
62210              * @event afteredit
62211              * Fires after a cell is edited. <br />
62212              * <ul style="padding:5px;padding-left:16px;">
62213              * <li>grid - This grid</li>
62214              * <li>record - The record being edited</li>
62215              * <li>field - The field name being edited</li>
62216              * <li>value - The value being set</li>
62217              * <li>originalValue - The original value for the field, before the edit.</li>
62218              * <li>row - The grid row index</li>
62219              * <li>column - The grid column index</li>
62220              * </ul>
62221              * @param {Object} e An edit event (see above for description)
62222              */
62223             "afteredit" : true,
62224             /**
62225              * @event validateedit
62226              * Fires after a cell is edited, but before the value is set in the record. 
62227          * You can use this to modify the value being set in the field, Return false
62228              * to cancel the change. The edit event object has the following properties <br />
62229              * <ul style="padding:5px;padding-left:16px;">
62230          * <li>editor - This editor</li>
62231              * <li>grid - This grid</li>
62232              * <li>record - The record being edited</li>
62233              * <li>field - The field name being edited</li>
62234              * <li>value - The value being set</li>
62235              * <li>originalValue - The original value for the field, before the edit.</li>
62236              * <li>row - The grid row index</li>
62237              * <li>column - The grid column index</li>
62238              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62239              * </ul>
62240              * @param {Object} e An edit event (see above for description)
62241              */
62242             "validateedit" : true
62243         });
62244     this.on("bodyscroll", this.stopEditing,  this);
62245     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62246 };
62247
62248 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62249     /**
62250      * @cfg {Number} clicksToEdit
62251      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62252      */
62253     clicksToEdit: 2,
62254
62255     // private
62256     isEditor : true,
62257     // private
62258     trackMouseOver: false, // causes very odd FF errors
62259
62260     onCellDblClick : function(g, row, col){
62261         this.startEditing(row, col);
62262     },
62263
62264     onEditComplete : function(ed, value, startValue){
62265         this.editing = false;
62266         this.activeEditor = null;
62267         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62268         var r = ed.record;
62269         var field = this.colModel.getDataIndex(ed.col);
62270         var e = {
62271             grid: this,
62272             record: r,
62273             field: field,
62274             originalValue: startValue,
62275             value: value,
62276             row: ed.row,
62277             column: ed.col,
62278             cancel:false,
62279             editor: ed
62280         };
62281         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62282         cell.show();
62283           
62284         if(String(value) !== String(startValue)){
62285             
62286             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62287                 r.set(field, e.value);
62288                 // if we are dealing with a combo box..
62289                 // then we also set the 'name' colum to be the displayField
62290                 if (ed.field.displayField && ed.field.name) {
62291                     r.set(ed.field.name, ed.field.el.dom.value);
62292                 }
62293                 
62294                 delete e.cancel; //?? why!!!
62295                 this.fireEvent("afteredit", e);
62296             }
62297         } else {
62298             this.fireEvent("afteredit", e); // always fire it!
62299         }
62300         this.view.focusCell(ed.row, ed.col);
62301     },
62302
62303     /**
62304      * Starts editing the specified for the specified row/column
62305      * @param {Number} rowIndex
62306      * @param {Number} colIndex
62307      */
62308     startEditing : function(row, col){
62309         this.stopEditing();
62310         if(this.colModel.isCellEditable(col, row)){
62311             this.view.ensureVisible(row, col, true);
62312           
62313             var r = this.dataSource.getAt(row);
62314             var field = this.colModel.getDataIndex(col);
62315             var cell = Roo.get(this.view.getCell(row,col));
62316             var e = {
62317                 grid: this,
62318                 record: r,
62319                 field: field,
62320                 value: r.data[field],
62321                 row: row,
62322                 column: col,
62323                 cancel:false 
62324             };
62325             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62326                 this.editing = true;
62327                 var ed = this.colModel.getCellEditor(col, row);
62328                 
62329                 if (!ed) {
62330                     return;
62331                 }
62332                 if(!ed.rendered){
62333                     ed.render(ed.parentEl || document.body);
62334                 }
62335                 ed.field.reset();
62336                
62337                 cell.hide();
62338                 
62339                 (function(){ // complex but required for focus issues in safari, ie and opera
62340                     ed.row = row;
62341                     ed.col = col;
62342                     ed.record = r;
62343                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62344                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62345                     this.activeEditor = ed;
62346                     var v = r.data[field];
62347                     ed.startEdit(this.view.getCell(row, col), v);
62348                     // combo's with 'displayField and name set
62349                     if (ed.field.displayField && ed.field.name) {
62350                         ed.field.el.dom.value = r.data[ed.field.name];
62351                     }
62352                     
62353                     
62354                 }).defer(50, this);
62355             }
62356         }
62357     },
62358         
62359     /**
62360      * Stops any active editing
62361      */
62362     stopEditing : function(){
62363         if(this.activeEditor){
62364             this.activeEditor.completeEdit();
62365         }
62366         this.activeEditor = null;
62367     },
62368         
62369          /**
62370      * Called to get grid's drag proxy text, by default returns this.ddText.
62371      * @return {String}
62372      */
62373     getDragDropText : function(){
62374         var count = this.selModel.getSelectedCell() ? 1 : 0;
62375         return String.format(this.ddText, count, count == 1 ? '' : 's');
62376     }
62377         
62378 });/*
62379  * Based on:
62380  * Ext JS Library 1.1.1
62381  * Copyright(c) 2006-2007, Ext JS, LLC.
62382  *
62383  * Originally Released Under LGPL - original licence link has changed is not relivant.
62384  *
62385  * Fork - LGPL
62386  * <script type="text/javascript">
62387  */
62388
62389 // private - not really -- you end up using it !
62390 // This is a support class used internally by the Grid components
62391
62392 /**
62393  * @class Roo.grid.GridEditor
62394  * @extends Roo.Editor
62395  * Class for creating and editable grid elements.
62396  * @param {Object} config any settings (must include field)
62397  */
62398 Roo.grid.GridEditor = function(field, config){
62399     if (!config && field.field) {
62400         config = field;
62401         field = Roo.factory(config.field, Roo.form);
62402     }
62403     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62404     field.monitorTab = false;
62405 };
62406
62407 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62408     
62409     /**
62410      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62411      */
62412     
62413     alignment: "tl-tl",
62414     autoSize: "width",
62415     hideEl : false,
62416     cls: "x-small-editor x-grid-editor",
62417     shim:false,
62418     shadow:"frame"
62419 });/*
62420  * Based on:
62421  * Ext JS Library 1.1.1
62422  * Copyright(c) 2006-2007, Ext JS, LLC.
62423  *
62424  * Originally Released Under LGPL - original licence link has changed is not relivant.
62425  *
62426  * Fork - LGPL
62427  * <script type="text/javascript">
62428  */
62429   
62430
62431   
62432 Roo.grid.PropertyRecord = Roo.data.Record.create([
62433     {name:'name',type:'string'},  'value'
62434 ]);
62435
62436
62437 Roo.grid.PropertyStore = function(grid, source){
62438     this.grid = grid;
62439     this.store = new Roo.data.Store({
62440         recordType : Roo.grid.PropertyRecord
62441     });
62442     this.store.on('update', this.onUpdate,  this);
62443     if(source){
62444         this.setSource(source);
62445     }
62446     Roo.grid.PropertyStore.superclass.constructor.call(this);
62447 };
62448
62449
62450
62451 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62452     setSource : function(o){
62453         this.source = o;
62454         this.store.removeAll();
62455         var data = [];
62456         for(var k in o){
62457             if(this.isEditableValue(o[k])){
62458                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62459             }
62460         }
62461         this.store.loadRecords({records: data}, {}, true);
62462     },
62463
62464     onUpdate : function(ds, record, type){
62465         if(type == Roo.data.Record.EDIT){
62466             var v = record.data['value'];
62467             var oldValue = record.modified['value'];
62468             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62469                 this.source[record.id] = v;
62470                 record.commit();
62471                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62472             }else{
62473                 record.reject();
62474             }
62475         }
62476     },
62477
62478     getProperty : function(row){
62479        return this.store.getAt(row);
62480     },
62481
62482     isEditableValue: function(val){
62483         if(val && val instanceof Date){
62484             return true;
62485         }else if(typeof val == 'object' || typeof val == 'function'){
62486             return false;
62487         }
62488         return true;
62489     },
62490
62491     setValue : function(prop, value){
62492         this.source[prop] = value;
62493         this.store.getById(prop).set('value', value);
62494     },
62495
62496     getSource : function(){
62497         return this.source;
62498     }
62499 });
62500
62501 Roo.grid.PropertyColumnModel = function(grid, store){
62502     this.grid = grid;
62503     var g = Roo.grid;
62504     g.PropertyColumnModel.superclass.constructor.call(this, [
62505         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62506         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62507     ]);
62508     this.store = store;
62509     this.bselect = Roo.DomHelper.append(document.body, {
62510         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62511             {tag: 'option', value: 'true', html: 'true'},
62512             {tag: 'option', value: 'false', html: 'false'}
62513         ]
62514     });
62515     Roo.id(this.bselect);
62516     var f = Roo.form;
62517     this.editors = {
62518         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62519         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62520         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62521         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62522         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62523     };
62524     this.renderCellDelegate = this.renderCell.createDelegate(this);
62525     this.renderPropDelegate = this.renderProp.createDelegate(this);
62526 };
62527
62528 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62529     
62530     
62531     nameText : 'Name',
62532     valueText : 'Value',
62533     
62534     dateFormat : 'm/j/Y',
62535     
62536     
62537     renderDate : function(dateVal){
62538         return dateVal.dateFormat(this.dateFormat);
62539     },
62540
62541     renderBool : function(bVal){
62542         return bVal ? 'true' : 'false';
62543     },
62544
62545     isCellEditable : function(colIndex, rowIndex){
62546         return colIndex == 1;
62547     },
62548
62549     getRenderer : function(col){
62550         return col == 1 ?
62551             this.renderCellDelegate : this.renderPropDelegate;
62552     },
62553
62554     renderProp : function(v){
62555         return this.getPropertyName(v);
62556     },
62557
62558     renderCell : function(val){
62559         var rv = val;
62560         if(val instanceof Date){
62561             rv = this.renderDate(val);
62562         }else if(typeof val == 'boolean'){
62563             rv = this.renderBool(val);
62564         }
62565         return Roo.util.Format.htmlEncode(rv);
62566     },
62567
62568     getPropertyName : function(name){
62569         var pn = this.grid.propertyNames;
62570         return pn && pn[name] ? pn[name] : name;
62571     },
62572
62573     getCellEditor : function(colIndex, rowIndex){
62574         var p = this.store.getProperty(rowIndex);
62575         var n = p.data['name'], val = p.data['value'];
62576         
62577         if(typeof(this.grid.customEditors[n]) == 'string'){
62578             return this.editors[this.grid.customEditors[n]];
62579         }
62580         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62581             return this.grid.customEditors[n];
62582         }
62583         if(val instanceof Date){
62584             return this.editors['date'];
62585         }else if(typeof val == 'number'){
62586             return this.editors['number'];
62587         }else if(typeof val == 'boolean'){
62588             return this.editors['boolean'];
62589         }else{
62590             return this.editors['string'];
62591         }
62592     }
62593 });
62594
62595 /**
62596  * @class Roo.grid.PropertyGrid
62597  * @extends Roo.grid.EditorGrid
62598  * This class represents the  interface of a component based property grid control.
62599  * <br><br>Usage:<pre><code>
62600  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62601       
62602  });
62603  // set any options
62604  grid.render();
62605  * </code></pre>
62606   
62607  * @constructor
62608  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62609  * The container MUST have some type of size defined for the grid to fill. The container will be
62610  * automatically set to position relative if it isn't already.
62611  * @param {Object} config A config object that sets properties on this grid.
62612  */
62613 Roo.grid.PropertyGrid = function(container, config){
62614     config = config || {};
62615     var store = new Roo.grid.PropertyStore(this);
62616     this.store = store;
62617     var cm = new Roo.grid.PropertyColumnModel(this, store);
62618     store.store.sort('name', 'ASC');
62619     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62620         ds: store.store,
62621         cm: cm,
62622         enableColLock:false,
62623         enableColumnMove:false,
62624         stripeRows:false,
62625         trackMouseOver: false,
62626         clicksToEdit:1
62627     }, config));
62628     this.getGridEl().addClass('x-props-grid');
62629     this.lastEditRow = null;
62630     this.on('columnresize', this.onColumnResize, this);
62631     this.addEvents({
62632          /**
62633              * @event beforepropertychange
62634              * Fires before a property changes (return false to stop?)
62635              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62636              * @param {String} id Record Id
62637              * @param {String} newval New Value
62638          * @param {String} oldval Old Value
62639              */
62640         "beforepropertychange": true,
62641         /**
62642              * @event propertychange
62643              * Fires after a property changes
62644              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62645              * @param {String} id Record Id
62646              * @param {String} newval New Value
62647          * @param {String} oldval Old Value
62648              */
62649         "propertychange": true
62650     });
62651     this.customEditors = this.customEditors || {};
62652 };
62653 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62654     
62655      /**
62656      * @cfg {Object} customEditors map of colnames=> custom editors.
62657      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62658      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62659      * false disables editing of the field.
62660          */
62661     
62662       /**
62663      * @cfg {Object} propertyNames map of property Names to their displayed value
62664          */
62665     
62666     render : function(){
62667         Roo.grid.PropertyGrid.superclass.render.call(this);
62668         this.autoSize.defer(100, this);
62669     },
62670
62671     autoSize : function(){
62672         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62673         if(this.view){
62674             this.view.fitColumns();
62675         }
62676     },
62677
62678     onColumnResize : function(){
62679         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62680         this.autoSize();
62681     },
62682     /**
62683      * Sets the data for the Grid
62684      * accepts a Key => Value object of all the elements avaiable.
62685      * @param {Object} data  to appear in grid.
62686      */
62687     setSource : function(source){
62688         this.store.setSource(source);
62689         //this.autoSize();
62690     },
62691     /**
62692      * Gets all the data from the grid.
62693      * @return {Object} data  data stored in grid
62694      */
62695     getSource : function(){
62696         return this.store.getSource();
62697     }
62698 });/*
62699   
62700  * Licence LGPL
62701  
62702  */
62703  
62704 /**
62705  * @class Roo.grid.Calendar
62706  * @extends Roo.grid.Grid
62707  * This class extends the Grid to provide a calendar widget
62708  * <br><br>Usage:<pre><code>
62709  var grid = new Roo.grid.Calendar("my-container-id", {
62710      ds: myDataStore,
62711      cm: myColModel,
62712      selModel: mySelectionModel,
62713      autoSizeColumns: true,
62714      monitorWindowResize: false,
62715      trackMouseOver: true
62716      eventstore : real data store..
62717  });
62718  // set any options
62719  grid.render();
62720   
62721   * @constructor
62722  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62723  * The container MUST have some type of size defined for the grid to fill. The container will be
62724  * automatically set to position relative if it isn't already.
62725  * @param {Object} config A config object that sets properties on this grid.
62726  */
62727 Roo.grid.Calendar = function(container, config){
62728         // initialize the container
62729         this.container = Roo.get(container);
62730         this.container.update("");
62731         this.container.setStyle("overflow", "hidden");
62732     this.container.addClass('x-grid-container');
62733
62734     this.id = this.container.id;
62735
62736     Roo.apply(this, config);
62737     // check and correct shorthanded configs
62738     
62739     var rows = [];
62740     var d =1;
62741     for (var r = 0;r < 6;r++) {
62742         
62743         rows[r]=[];
62744         for (var c =0;c < 7;c++) {
62745             rows[r][c]= '';
62746         }
62747     }
62748     if (this.eventStore) {
62749         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62750         this.eventStore.on('load',this.onLoad, this);
62751         this.eventStore.on('beforeload',this.clearEvents, this);
62752          
62753     }
62754     
62755     this.dataSource = new Roo.data.Store({
62756             proxy: new Roo.data.MemoryProxy(rows),
62757             reader: new Roo.data.ArrayReader({}, [
62758                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62759     });
62760
62761     this.dataSource.load();
62762     this.ds = this.dataSource;
62763     this.ds.xmodule = this.xmodule || false;
62764     
62765     
62766     var cellRender = function(v,x,r)
62767     {
62768         return String.format(
62769             '<div class="fc-day  fc-widget-content"><div>' +
62770                 '<div class="fc-event-container"></div>' +
62771                 '<div class="fc-day-number">{0}</div>'+
62772                 
62773                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62774             '</div></div>', v);
62775     
62776     }
62777     
62778     
62779     this.colModel = new Roo.grid.ColumnModel( [
62780         {
62781             xtype: 'ColumnModel',
62782             xns: Roo.grid,
62783             dataIndex : 'weekday0',
62784             header : 'Sunday',
62785             renderer : cellRender
62786         },
62787         {
62788             xtype: 'ColumnModel',
62789             xns: Roo.grid,
62790             dataIndex : 'weekday1',
62791             header : 'Monday',
62792             renderer : cellRender
62793         },
62794         {
62795             xtype: 'ColumnModel',
62796             xns: Roo.grid,
62797             dataIndex : 'weekday2',
62798             header : 'Tuesday',
62799             renderer : cellRender
62800         },
62801         {
62802             xtype: 'ColumnModel',
62803             xns: Roo.grid,
62804             dataIndex : 'weekday3',
62805             header : 'Wednesday',
62806             renderer : cellRender
62807         },
62808         {
62809             xtype: 'ColumnModel',
62810             xns: Roo.grid,
62811             dataIndex : 'weekday4',
62812             header : 'Thursday',
62813             renderer : cellRender
62814         },
62815         {
62816             xtype: 'ColumnModel',
62817             xns: Roo.grid,
62818             dataIndex : 'weekday5',
62819             header : 'Friday',
62820             renderer : cellRender
62821         },
62822         {
62823             xtype: 'ColumnModel',
62824             xns: Roo.grid,
62825             dataIndex : 'weekday6',
62826             header : 'Saturday',
62827             renderer : cellRender
62828         }
62829     ]);
62830     this.cm = this.colModel;
62831     this.cm.xmodule = this.xmodule || false;
62832  
62833         
62834           
62835     //this.selModel = new Roo.grid.CellSelectionModel();
62836     //this.sm = this.selModel;
62837     //this.selModel.init(this);
62838     
62839     
62840     if(this.width){
62841         this.container.setWidth(this.width);
62842     }
62843
62844     if(this.height){
62845         this.container.setHeight(this.height);
62846     }
62847     /** @private */
62848         this.addEvents({
62849         // raw events
62850         /**
62851          * @event click
62852          * The raw click event for the entire grid.
62853          * @param {Roo.EventObject} e
62854          */
62855         "click" : true,
62856         /**
62857          * @event dblclick
62858          * The raw dblclick event for the entire grid.
62859          * @param {Roo.EventObject} e
62860          */
62861         "dblclick" : true,
62862         /**
62863          * @event contextmenu
62864          * The raw contextmenu event for the entire grid.
62865          * @param {Roo.EventObject} e
62866          */
62867         "contextmenu" : true,
62868         /**
62869          * @event mousedown
62870          * The raw mousedown event for the entire grid.
62871          * @param {Roo.EventObject} e
62872          */
62873         "mousedown" : true,
62874         /**
62875          * @event mouseup
62876          * The raw mouseup event for the entire grid.
62877          * @param {Roo.EventObject} e
62878          */
62879         "mouseup" : true,
62880         /**
62881          * @event mouseover
62882          * The raw mouseover event for the entire grid.
62883          * @param {Roo.EventObject} e
62884          */
62885         "mouseover" : true,
62886         /**
62887          * @event mouseout
62888          * The raw mouseout event for the entire grid.
62889          * @param {Roo.EventObject} e
62890          */
62891         "mouseout" : true,
62892         /**
62893          * @event keypress
62894          * The raw keypress event for the entire grid.
62895          * @param {Roo.EventObject} e
62896          */
62897         "keypress" : true,
62898         /**
62899          * @event keydown
62900          * The raw keydown event for the entire grid.
62901          * @param {Roo.EventObject} e
62902          */
62903         "keydown" : true,
62904
62905         // custom events
62906
62907         /**
62908          * @event cellclick
62909          * Fires when a cell is clicked
62910          * @param {Grid} this
62911          * @param {Number} rowIndex
62912          * @param {Number} columnIndex
62913          * @param {Roo.EventObject} e
62914          */
62915         "cellclick" : true,
62916         /**
62917          * @event celldblclick
62918          * Fires when a cell is double clicked
62919          * @param {Grid} this
62920          * @param {Number} rowIndex
62921          * @param {Number} columnIndex
62922          * @param {Roo.EventObject} e
62923          */
62924         "celldblclick" : true,
62925         /**
62926          * @event rowclick
62927          * Fires when a row is clicked
62928          * @param {Grid} this
62929          * @param {Number} rowIndex
62930          * @param {Roo.EventObject} e
62931          */
62932         "rowclick" : true,
62933         /**
62934          * @event rowdblclick
62935          * Fires when a row is double clicked
62936          * @param {Grid} this
62937          * @param {Number} rowIndex
62938          * @param {Roo.EventObject} e
62939          */
62940         "rowdblclick" : true,
62941         /**
62942          * @event headerclick
62943          * Fires when a header is clicked
62944          * @param {Grid} this
62945          * @param {Number} columnIndex
62946          * @param {Roo.EventObject} e
62947          */
62948         "headerclick" : true,
62949         /**
62950          * @event headerdblclick
62951          * Fires when a header cell is double clicked
62952          * @param {Grid} this
62953          * @param {Number} columnIndex
62954          * @param {Roo.EventObject} e
62955          */
62956         "headerdblclick" : true,
62957         /**
62958          * @event rowcontextmenu
62959          * Fires when a row is right clicked
62960          * @param {Grid} this
62961          * @param {Number} rowIndex
62962          * @param {Roo.EventObject} e
62963          */
62964         "rowcontextmenu" : true,
62965         /**
62966          * @event cellcontextmenu
62967          * Fires when a cell is right clicked
62968          * @param {Grid} this
62969          * @param {Number} rowIndex
62970          * @param {Number} cellIndex
62971          * @param {Roo.EventObject} e
62972          */
62973          "cellcontextmenu" : true,
62974         /**
62975          * @event headercontextmenu
62976          * Fires when a header is right clicked
62977          * @param {Grid} this
62978          * @param {Number} columnIndex
62979          * @param {Roo.EventObject} e
62980          */
62981         "headercontextmenu" : true,
62982         /**
62983          * @event bodyscroll
62984          * Fires when the body element is scrolled
62985          * @param {Number} scrollLeft
62986          * @param {Number} scrollTop
62987          */
62988         "bodyscroll" : true,
62989         /**
62990          * @event columnresize
62991          * Fires when the user resizes a column
62992          * @param {Number} columnIndex
62993          * @param {Number} newSize
62994          */
62995         "columnresize" : true,
62996         /**
62997          * @event columnmove
62998          * Fires when the user moves a column
62999          * @param {Number} oldIndex
63000          * @param {Number} newIndex
63001          */
63002         "columnmove" : true,
63003         /**
63004          * @event startdrag
63005          * Fires when row(s) start being dragged
63006          * @param {Grid} this
63007          * @param {Roo.GridDD} dd The drag drop object
63008          * @param {event} e The raw browser event
63009          */
63010         "startdrag" : true,
63011         /**
63012          * @event enddrag
63013          * Fires when a drag operation is complete
63014          * @param {Grid} this
63015          * @param {Roo.GridDD} dd The drag drop object
63016          * @param {event} e The raw browser event
63017          */
63018         "enddrag" : true,
63019         /**
63020          * @event dragdrop
63021          * Fires when dragged row(s) are dropped on a valid DD target
63022          * @param {Grid} this
63023          * @param {Roo.GridDD} dd The drag drop object
63024          * @param {String} targetId The target drag drop object
63025          * @param {event} e The raw browser event
63026          */
63027         "dragdrop" : true,
63028         /**
63029          * @event dragover
63030          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63031          * @param {Grid} this
63032          * @param {Roo.GridDD} dd The drag drop object
63033          * @param {String} targetId The target drag drop object
63034          * @param {event} e The raw browser event
63035          */
63036         "dragover" : true,
63037         /**
63038          * @event dragenter
63039          *  Fires when the dragged row(s) first cross another DD target while being dragged
63040          * @param {Grid} this
63041          * @param {Roo.GridDD} dd The drag drop object
63042          * @param {String} targetId The target drag drop object
63043          * @param {event} e The raw browser event
63044          */
63045         "dragenter" : true,
63046         /**
63047          * @event dragout
63048          * Fires when the dragged row(s) leave another DD target while being dragged
63049          * @param {Grid} this
63050          * @param {Roo.GridDD} dd The drag drop object
63051          * @param {String} targetId The target drag drop object
63052          * @param {event} e The raw browser event
63053          */
63054         "dragout" : true,
63055         /**
63056          * @event rowclass
63057          * Fires when a row is rendered, so you can change add a style to it.
63058          * @param {GridView} gridview   The grid view
63059          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63060          */
63061         'rowclass' : true,
63062
63063         /**
63064          * @event render
63065          * Fires when the grid is rendered
63066          * @param {Grid} grid
63067          */
63068         'render' : true,
63069             /**
63070              * @event select
63071              * Fires when a date is selected
63072              * @param {DatePicker} this
63073              * @param {Date} date The selected date
63074              */
63075         'select': true,
63076         /**
63077              * @event monthchange
63078              * Fires when the displayed month changes 
63079              * @param {DatePicker} this
63080              * @param {Date} date The selected month
63081              */
63082         'monthchange': true,
63083         /**
63084              * @event evententer
63085              * Fires when mouse over an event
63086              * @param {Calendar} this
63087              * @param {event} Event
63088              */
63089         'evententer': true,
63090         /**
63091              * @event eventleave
63092              * Fires when the mouse leaves an
63093              * @param {Calendar} this
63094              * @param {event}
63095              */
63096         'eventleave': true,
63097         /**
63098              * @event eventclick
63099              * Fires when the mouse click an
63100              * @param {Calendar} this
63101              * @param {event}
63102              */
63103         'eventclick': true,
63104         /**
63105              * @event eventrender
63106              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63107              * @param {Calendar} this
63108              * @param {data} data to be modified
63109              */
63110         'eventrender': true
63111         
63112     });
63113
63114     Roo.grid.Grid.superclass.constructor.call(this);
63115     this.on('render', function() {
63116         this.view.el.addClass('x-grid-cal'); 
63117         
63118         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63119
63120     },this);
63121     
63122     if (!Roo.grid.Calendar.style) {
63123         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63124             
63125             
63126             '.x-grid-cal .x-grid-col' :  {
63127                 height: 'auto !important',
63128                 'vertical-align': 'top'
63129             },
63130             '.x-grid-cal  .fc-event-hori' : {
63131                 height: '14px'
63132             }
63133              
63134             
63135         }, Roo.id());
63136     }
63137
63138     
63139     
63140 };
63141 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63142     /**
63143      * @cfg {Store} eventStore The store that loads events.
63144      */
63145     eventStore : 25,
63146
63147      
63148     activeDate : false,
63149     startDay : 0,
63150     autoWidth : true,
63151     monitorWindowResize : false,
63152
63153     
63154     resizeColumns : function() {
63155         var col = (this.view.el.getWidth() / 7) - 3;
63156         // loop through cols, and setWidth
63157         for(var i =0 ; i < 7 ; i++){
63158             this.cm.setColumnWidth(i, col);
63159         }
63160     },
63161      setDate :function(date) {
63162         
63163         Roo.log('setDate?');
63164         
63165         this.resizeColumns();
63166         var vd = this.activeDate;
63167         this.activeDate = date;
63168 //        if(vd && this.el){
63169 //            var t = date.getTime();
63170 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63171 //                Roo.log('using add remove');
63172 //                
63173 //                this.fireEvent('monthchange', this, date);
63174 //                
63175 //                this.cells.removeClass("fc-state-highlight");
63176 //                this.cells.each(function(c){
63177 //                   if(c.dateValue == t){
63178 //                       c.addClass("fc-state-highlight");
63179 //                       setTimeout(function(){
63180 //                            try{c.dom.firstChild.focus();}catch(e){}
63181 //                       }, 50);
63182 //                       return false;
63183 //                   }
63184 //                   return true;
63185 //                });
63186 //                return;
63187 //            }
63188 //        }
63189         
63190         var days = date.getDaysInMonth();
63191         
63192         var firstOfMonth = date.getFirstDateOfMonth();
63193         var startingPos = firstOfMonth.getDay()-this.startDay;
63194         
63195         if(startingPos < this.startDay){
63196             startingPos += 7;
63197         }
63198         
63199         var pm = date.add(Date.MONTH, -1);
63200         var prevStart = pm.getDaysInMonth()-startingPos;
63201 //        
63202         
63203         
63204         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63205         
63206         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63207         //this.cells.addClassOnOver('fc-state-hover');
63208         
63209         var cells = this.cells.elements;
63210         var textEls = this.textNodes;
63211         
63212         //Roo.each(cells, function(cell){
63213         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63214         //});
63215         
63216         days += startingPos;
63217
63218         // convert everything to numbers so it's fast
63219         var day = 86400000;
63220         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63221         //Roo.log(d);
63222         //Roo.log(pm);
63223         //Roo.log(prevStart);
63224         
63225         var today = new Date().clearTime().getTime();
63226         var sel = date.clearTime().getTime();
63227         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63228         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63229         var ddMatch = this.disabledDatesRE;
63230         var ddText = this.disabledDatesText;
63231         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63232         var ddaysText = this.disabledDaysText;
63233         var format = this.format;
63234         
63235         var setCellClass = function(cal, cell){
63236             
63237             //Roo.log('set Cell Class');
63238             cell.title = "";
63239             var t = d.getTime();
63240             
63241             //Roo.log(d);
63242             
63243             
63244             cell.dateValue = t;
63245             if(t == today){
63246                 cell.className += " fc-today";
63247                 cell.className += " fc-state-highlight";
63248                 cell.title = cal.todayText;
63249             }
63250             if(t == sel){
63251                 // disable highlight in other month..
63252                 cell.className += " fc-state-highlight";
63253                 
63254             }
63255             // disabling
63256             if(t < min) {
63257                 //cell.className = " fc-state-disabled";
63258                 cell.title = cal.minText;
63259                 return;
63260             }
63261             if(t > max) {
63262                 //cell.className = " fc-state-disabled";
63263                 cell.title = cal.maxText;
63264                 return;
63265             }
63266             if(ddays){
63267                 if(ddays.indexOf(d.getDay()) != -1){
63268                     // cell.title = ddaysText;
63269                    // cell.className = " fc-state-disabled";
63270                 }
63271             }
63272             if(ddMatch && format){
63273                 var fvalue = d.dateFormat(format);
63274                 if(ddMatch.test(fvalue)){
63275                     cell.title = ddText.replace("%0", fvalue);
63276                    cell.className = " fc-state-disabled";
63277                 }
63278             }
63279             
63280             if (!cell.initialClassName) {
63281                 cell.initialClassName = cell.dom.className;
63282             }
63283             
63284             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63285         };
63286
63287         var i = 0;
63288         
63289         for(; i < startingPos; i++) {
63290             cells[i].dayName =  (++prevStart);
63291             Roo.log(textEls[i]);
63292             d.setDate(d.getDate()+1);
63293             
63294             //cells[i].className = "fc-past fc-other-month";
63295             setCellClass(this, cells[i]);
63296         }
63297         
63298         var intDay = 0;
63299         
63300         for(; i < days; i++){
63301             intDay = i - startingPos + 1;
63302             cells[i].dayName =  (intDay);
63303             d.setDate(d.getDate()+1);
63304             
63305             cells[i].className = ''; // "x-date-active";
63306             setCellClass(this, cells[i]);
63307         }
63308         var extraDays = 0;
63309         
63310         for(; i < 42; i++) {
63311             //textEls[i].innerHTML = (++extraDays);
63312             
63313             d.setDate(d.getDate()+1);
63314             cells[i].dayName = (++extraDays);
63315             cells[i].className = "fc-future fc-other-month";
63316             setCellClass(this, cells[i]);
63317         }
63318         
63319         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63320         
63321         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63322         
63323         // this will cause all the cells to mis
63324         var rows= [];
63325         var i =0;
63326         for (var r = 0;r < 6;r++) {
63327             for (var c =0;c < 7;c++) {
63328                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63329             }    
63330         }
63331         
63332         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63333         for(i=0;i<cells.length;i++) {
63334             
63335             this.cells.elements[i].dayName = cells[i].dayName ;
63336             this.cells.elements[i].className = cells[i].className;
63337             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63338             this.cells.elements[i].title = cells[i].title ;
63339             this.cells.elements[i].dateValue = cells[i].dateValue ;
63340         }
63341         
63342         
63343         
63344         
63345         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63346         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63347         
63348         ////if(totalRows != 6){
63349             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63350            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63351        // }
63352         
63353         this.fireEvent('monthchange', this, date);
63354         
63355         
63356     },
63357  /**
63358      * Returns the grid's SelectionModel.
63359      * @return {SelectionModel}
63360      */
63361     getSelectionModel : function(){
63362         if(!this.selModel){
63363             this.selModel = new Roo.grid.CellSelectionModel();
63364         }
63365         return this.selModel;
63366     },
63367
63368     load: function() {
63369         this.eventStore.load()
63370         
63371         
63372         
63373     },
63374     
63375     findCell : function(dt) {
63376         dt = dt.clearTime().getTime();
63377         var ret = false;
63378         this.cells.each(function(c){
63379             //Roo.log("check " +c.dateValue + '?=' + dt);
63380             if(c.dateValue == dt){
63381                 ret = c;
63382                 return false;
63383             }
63384             return true;
63385         });
63386         
63387         return ret;
63388     },
63389     
63390     findCells : function(rec) {
63391         var s = rec.data.start_dt.clone().clearTime().getTime();
63392        // Roo.log(s);
63393         var e= rec.data.end_dt.clone().clearTime().getTime();
63394        // Roo.log(e);
63395         var ret = [];
63396         this.cells.each(function(c){
63397              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63398             
63399             if(c.dateValue > e){
63400                 return ;
63401             }
63402             if(c.dateValue < s){
63403                 return ;
63404             }
63405             ret.push(c);
63406         });
63407         
63408         return ret;    
63409     },
63410     
63411     findBestRow: function(cells)
63412     {
63413         var ret = 0;
63414         
63415         for (var i =0 ; i < cells.length;i++) {
63416             ret  = Math.max(cells[i].rows || 0,ret);
63417         }
63418         return ret;
63419         
63420     },
63421     
63422     
63423     addItem : function(rec)
63424     {
63425         // look for vertical location slot in
63426         var cells = this.findCells(rec);
63427         
63428         rec.row = this.findBestRow(cells);
63429         
63430         // work out the location.
63431         
63432         var crow = false;
63433         var rows = [];
63434         for(var i =0; i < cells.length; i++) {
63435             if (!crow) {
63436                 crow = {
63437                     start : cells[i],
63438                     end :  cells[i]
63439                 };
63440                 continue;
63441             }
63442             if (crow.start.getY() == cells[i].getY()) {
63443                 // on same row.
63444                 crow.end = cells[i];
63445                 continue;
63446             }
63447             // different row.
63448             rows.push(crow);
63449             crow = {
63450                 start: cells[i],
63451                 end : cells[i]
63452             };
63453             
63454         }
63455         
63456         rows.push(crow);
63457         rec.els = [];
63458         rec.rows = rows;
63459         rec.cells = cells;
63460         for (var i = 0; i < cells.length;i++) {
63461             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63462             
63463         }
63464         
63465         
63466     },
63467     
63468     clearEvents: function() {
63469         
63470         if (!this.eventStore.getCount()) {
63471             return;
63472         }
63473         // reset number of rows in cells.
63474         Roo.each(this.cells.elements, function(c){
63475             c.rows = 0;
63476         });
63477         
63478         this.eventStore.each(function(e) {
63479             this.clearEvent(e);
63480         },this);
63481         
63482     },
63483     
63484     clearEvent : function(ev)
63485     {
63486         if (ev.els) {
63487             Roo.each(ev.els, function(el) {
63488                 el.un('mouseenter' ,this.onEventEnter, this);
63489                 el.un('mouseleave' ,this.onEventLeave, this);
63490                 el.remove();
63491             },this);
63492             ev.els = [];
63493         }
63494     },
63495     
63496     
63497     renderEvent : function(ev,ctr) {
63498         if (!ctr) {
63499              ctr = this.view.el.select('.fc-event-container',true).first();
63500         }
63501         
63502          
63503         this.clearEvent(ev);
63504             //code
63505        
63506         
63507         
63508         ev.els = [];
63509         var cells = ev.cells;
63510         var rows = ev.rows;
63511         this.fireEvent('eventrender', this, ev);
63512         
63513         for(var i =0; i < rows.length; i++) {
63514             
63515             cls = '';
63516             if (i == 0) {
63517                 cls += ' fc-event-start';
63518             }
63519             if ((i+1) == rows.length) {
63520                 cls += ' fc-event-end';
63521             }
63522             
63523             //Roo.log(ev.data);
63524             // how many rows should it span..
63525             var cg = this.eventTmpl.append(ctr,Roo.apply({
63526                 fccls : cls
63527                 
63528             }, ev.data) , true);
63529             
63530             
63531             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63532             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63533             cg.on('click', this.onEventClick, this, ev);
63534             
63535             ev.els.push(cg);
63536             
63537             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63538             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63539             //Roo.log(cg);
63540              
63541             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63542             cg.setWidth(ebox.right - sbox.x -2);
63543         }
63544     },
63545     
63546     renderEvents: function()
63547     {   
63548         // first make sure there is enough space..
63549         
63550         if (!this.eventTmpl) {
63551             this.eventTmpl = new Roo.Template(
63552                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63553                     '<div class="fc-event-inner">' +
63554                         '<span class="fc-event-time">{time}</span>' +
63555                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63556                     '</div>' +
63557                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63558                 '</div>'
63559             );
63560                 
63561         }
63562                
63563         
63564         
63565         this.cells.each(function(c) {
63566             //Roo.log(c.select('.fc-day-content div',true).first());
63567             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63568         });
63569         
63570         var ctr = this.view.el.select('.fc-event-container',true).first();
63571         
63572         var cls;
63573         this.eventStore.each(function(ev){
63574             
63575             this.renderEvent(ev);
63576              
63577              
63578         }, this);
63579         this.view.layout();
63580         
63581     },
63582     
63583     onEventEnter: function (e, el,event,d) {
63584         this.fireEvent('evententer', this, el, event);
63585     },
63586     
63587     onEventLeave: function (e, el,event,d) {
63588         this.fireEvent('eventleave', this, el, event);
63589     },
63590     
63591     onEventClick: function (e, el,event,d) {
63592         this.fireEvent('eventclick', this, el, event);
63593     },
63594     
63595     onMonthChange: function () {
63596         this.store.load();
63597     },
63598     
63599     onLoad: function () {
63600         
63601         //Roo.log('calendar onload');
63602 //         
63603         if(this.eventStore.getCount() > 0){
63604             
63605            
63606             
63607             this.eventStore.each(function(d){
63608                 
63609                 
63610                 // FIXME..
63611                 var add =   d.data;
63612                 if (typeof(add.end_dt) == 'undefined')  {
63613                     Roo.log("Missing End time in calendar data: ");
63614                     Roo.log(d);
63615                     return;
63616                 }
63617                 if (typeof(add.start_dt) == 'undefined')  {
63618                     Roo.log("Missing Start time in calendar data: ");
63619                     Roo.log(d);
63620                     return;
63621                 }
63622                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63623                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63624                 add.id = add.id || d.id;
63625                 add.title = add.title || '??';
63626                 
63627                 this.addItem(d);
63628                 
63629              
63630             },this);
63631         }
63632         
63633         this.renderEvents();
63634     }
63635     
63636
63637 });
63638 /*
63639  grid : {
63640                 xtype: 'Grid',
63641                 xns: Roo.grid,
63642                 listeners : {
63643                     render : function ()
63644                     {
63645                         _this.grid = this;
63646                         
63647                         if (!this.view.el.hasClass('course-timesheet')) {
63648                             this.view.el.addClass('course-timesheet');
63649                         }
63650                         if (this.tsStyle) {
63651                             this.ds.load({});
63652                             return; 
63653                         }
63654                         Roo.log('width');
63655                         Roo.log(_this.grid.view.el.getWidth());
63656                         
63657                         
63658                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63659                             '.course-timesheet .x-grid-row' : {
63660                                 height: '80px'
63661                             },
63662                             '.x-grid-row td' : {
63663                                 'vertical-align' : 0
63664                             },
63665                             '.course-edit-link' : {
63666                                 'color' : 'blue',
63667                                 'text-overflow' : 'ellipsis',
63668                                 'overflow' : 'hidden',
63669                                 'white-space' : 'nowrap',
63670                                 'cursor' : 'pointer'
63671                             },
63672                             '.sub-link' : {
63673                                 'color' : 'green'
63674                             },
63675                             '.de-act-sup-link' : {
63676                                 'color' : 'purple',
63677                                 'text-decoration' : 'line-through'
63678                             },
63679                             '.de-act-link' : {
63680                                 'color' : 'red',
63681                                 'text-decoration' : 'line-through'
63682                             },
63683                             '.course-timesheet .course-highlight' : {
63684                                 'border-top-style': 'dashed !important',
63685                                 'border-bottom-bottom': 'dashed !important'
63686                             },
63687                             '.course-timesheet .course-item' : {
63688                                 'font-family'   : 'tahoma, arial, helvetica',
63689                                 'font-size'     : '11px',
63690                                 'overflow'      : 'hidden',
63691                                 'padding-left'  : '10px',
63692                                 'padding-right' : '10px',
63693                                 'padding-top' : '10px' 
63694                             }
63695                             
63696                         }, Roo.id());
63697                                 this.ds.load({});
63698                     }
63699                 },
63700                 autoWidth : true,
63701                 monitorWindowResize : false,
63702                 cellrenderer : function(v,x,r)
63703                 {
63704                     return v;
63705                 },
63706                 sm : {
63707                     xtype: 'CellSelectionModel',
63708                     xns: Roo.grid
63709                 },
63710                 dataSource : {
63711                     xtype: 'Store',
63712                     xns: Roo.data,
63713                     listeners : {
63714                         beforeload : function (_self, options)
63715                         {
63716                             options.params = options.params || {};
63717                             options.params._month = _this.monthField.getValue();
63718                             options.params.limit = 9999;
63719                             options.params['sort'] = 'when_dt';    
63720                             options.params['dir'] = 'ASC';    
63721                             this.proxy.loadResponse = this.loadResponse;
63722                             Roo.log("load?");
63723                             //this.addColumns();
63724                         },
63725                         load : function (_self, records, options)
63726                         {
63727                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63728                                 // if you click on the translation.. you can edit it...
63729                                 var el = Roo.get(this);
63730                                 var id = el.dom.getAttribute('data-id');
63731                                 var d = el.dom.getAttribute('data-date');
63732                                 var t = el.dom.getAttribute('data-time');
63733                                 //var id = this.child('span').dom.textContent;
63734                                 
63735                                 //Roo.log(this);
63736                                 Pman.Dialog.CourseCalendar.show({
63737                                     id : id,
63738                                     when_d : d,
63739                                     when_t : t,
63740                                     productitem_active : id ? 1 : 0
63741                                 }, function() {
63742                                     _this.grid.ds.load({});
63743                                 });
63744                            
63745                            });
63746                            
63747                            _this.panel.fireEvent('resize', [ '', '' ]);
63748                         }
63749                     },
63750                     loadResponse : function(o, success, response){
63751                             // this is overridden on before load..
63752                             
63753                             Roo.log("our code?");       
63754                             //Roo.log(success);
63755                             //Roo.log(response)
63756                             delete this.activeRequest;
63757                             if(!success){
63758                                 this.fireEvent("loadexception", this, o, response);
63759                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63760                                 return;
63761                             }
63762                             var result;
63763                             try {
63764                                 result = o.reader.read(response);
63765                             }catch(e){
63766                                 Roo.log("load exception?");
63767                                 this.fireEvent("loadexception", this, o, response, e);
63768                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63769                                 return;
63770                             }
63771                             Roo.log("ready...");        
63772                             // loop through result.records;
63773                             // and set this.tdate[date] = [] << array of records..
63774                             _this.tdata  = {};
63775                             Roo.each(result.records, function(r){
63776                                 //Roo.log(r.data);
63777                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63778                                     _this.tdata[r.data.when_dt.format('j')] = [];
63779                                 }
63780                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63781                             });
63782                             
63783                             //Roo.log(_this.tdata);
63784                             
63785                             result.records = [];
63786                             result.totalRecords = 6;
63787                     
63788                             // let's generate some duumy records for the rows.
63789                             //var st = _this.dateField.getValue();
63790                             
63791                             // work out monday..
63792                             //st = st.add(Date.DAY, -1 * st.format('w'));
63793                             
63794                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63795                             
63796                             var firstOfMonth = date.getFirstDayOfMonth();
63797                             var days = date.getDaysInMonth();
63798                             var d = 1;
63799                             var firstAdded = false;
63800                             for (var i = 0; i < result.totalRecords ; i++) {
63801                                 //var d= st.add(Date.DAY, i);
63802                                 var row = {};
63803                                 var added = 0;
63804                                 for(var w = 0 ; w < 7 ; w++){
63805                                     if(!firstAdded && firstOfMonth != w){
63806                                         continue;
63807                                     }
63808                                     if(d > days){
63809                                         continue;
63810                                     }
63811                                     firstAdded = true;
63812                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63813                                     row['weekday'+w] = String.format(
63814                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63815                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63816                                                     d,
63817                                                     date.format('Y-m-')+dd
63818                                                 );
63819                                     added++;
63820                                     if(typeof(_this.tdata[d]) != 'undefined'){
63821                                         Roo.each(_this.tdata[d], function(r){
63822                                             var is_sub = '';
63823                                             var deactive = '';
63824                                             var id = r.id;
63825                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63826                                             if(r.parent_id*1>0){
63827                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63828                                                 id = r.parent_id;
63829                                             }
63830                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63831                                                 deactive = 'de-act-link';
63832                                             }
63833                                             
63834                                             row['weekday'+w] += String.format(
63835                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63836                                                     id, //0
63837                                                     r.product_id_name, //1
63838                                                     r.when_dt.format('h:ia'), //2
63839                                                     is_sub, //3
63840                                                     deactive, //4
63841                                                     desc // 5
63842                                             );
63843                                         });
63844                                     }
63845                                     d++;
63846                                 }
63847                                 
63848                                 // only do this if something added..
63849                                 if(added > 0){ 
63850                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63851                                 }
63852                                 
63853                                 
63854                                 // push it twice. (second one with an hour..
63855                                 
63856                             }
63857                             //Roo.log(result);
63858                             this.fireEvent("load", this, o, o.request.arg);
63859                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63860                         },
63861                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63862                     proxy : {
63863                         xtype: 'HttpProxy',
63864                         xns: Roo.data,
63865                         method : 'GET',
63866                         url : baseURL + '/Roo/Shop_course.php'
63867                     },
63868                     reader : {
63869                         xtype: 'JsonReader',
63870                         xns: Roo.data,
63871                         id : 'id',
63872                         fields : [
63873                             {
63874                                 'name': 'id',
63875                                 'type': 'int'
63876                             },
63877                             {
63878                                 'name': 'when_dt',
63879                                 'type': 'string'
63880                             },
63881                             {
63882                                 'name': 'end_dt',
63883                                 'type': 'string'
63884                             },
63885                             {
63886                                 'name': 'parent_id',
63887                                 'type': 'int'
63888                             },
63889                             {
63890                                 'name': 'product_id',
63891                                 'type': 'int'
63892                             },
63893                             {
63894                                 'name': 'productitem_id',
63895                                 'type': 'int'
63896                             },
63897                             {
63898                                 'name': 'guid',
63899                                 'type': 'int'
63900                             }
63901                         ]
63902                     }
63903                 },
63904                 toolbar : {
63905                     xtype: 'Toolbar',
63906                     xns: Roo,
63907                     items : [
63908                         {
63909                             xtype: 'Button',
63910                             xns: Roo.Toolbar,
63911                             listeners : {
63912                                 click : function (_self, e)
63913                                 {
63914                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63915                                     sd.setMonth(sd.getMonth()-1);
63916                                     _this.monthField.setValue(sd.format('Y-m-d'));
63917                                     _this.grid.ds.load({});
63918                                 }
63919                             },
63920                             text : "Back"
63921                         },
63922                         {
63923                             xtype: 'Separator',
63924                             xns: Roo.Toolbar
63925                         },
63926                         {
63927                             xtype: 'MonthField',
63928                             xns: Roo.form,
63929                             listeners : {
63930                                 render : function (_self)
63931                                 {
63932                                     _this.monthField = _self;
63933                                    // _this.monthField.set  today
63934                                 },
63935                                 select : function (combo, date)
63936                                 {
63937                                     _this.grid.ds.load({});
63938                                 }
63939                             },
63940                             value : (function() { return new Date(); })()
63941                         },
63942                         {
63943                             xtype: 'Separator',
63944                             xns: Roo.Toolbar
63945                         },
63946                         {
63947                             xtype: 'TextItem',
63948                             xns: Roo.Toolbar,
63949                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63950                         },
63951                         {
63952                             xtype: 'Fill',
63953                             xns: Roo.Toolbar
63954                         },
63955                         {
63956                             xtype: 'Button',
63957                             xns: Roo.Toolbar,
63958                             listeners : {
63959                                 click : function (_self, e)
63960                                 {
63961                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63962                                     sd.setMonth(sd.getMonth()+1);
63963                                     _this.monthField.setValue(sd.format('Y-m-d'));
63964                                     _this.grid.ds.load({});
63965                                 }
63966                             },
63967                             text : "Next"
63968                         }
63969                     ]
63970                 },
63971                  
63972             }
63973         };
63974         
63975         *//*
63976  * Based on:
63977  * Ext JS Library 1.1.1
63978  * Copyright(c) 2006-2007, Ext JS, LLC.
63979  *
63980  * Originally Released Under LGPL - original licence link has changed is not relivant.
63981  *
63982  * Fork - LGPL
63983  * <script type="text/javascript">
63984  */
63985  
63986 /**
63987  * @class Roo.LoadMask
63988  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63989  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63990  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63991  * element's UpdateManager load indicator and will be destroyed after the initial load.
63992  * @constructor
63993  * Create a new LoadMask
63994  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63995  * @param {Object} config The config object
63996  */
63997 Roo.LoadMask = function(el, config){
63998     this.el = Roo.get(el);
63999     Roo.apply(this, config);
64000     if(this.store){
64001         this.store.on('beforeload', this.onBeforeLoad, this);
64002         this.store.on('load', this.onLoad, this);
64003         this.store.on('loadexception', this.onLoadException, this);
64004         this.removeMask = false;
64005     }else{
64006         var um = this.el.getUpdateManager();
64007         um.showLoadIndicator = false; // disable the default indicator
64008         um.on('beforeupdate', this.onBeforeLoad, this);
64009         um.on('update', this.onLoad, this);
64010         um.on('failure', this.onLoad, this);
64011         this.removeMask = true;
64012     }
64013 };
64014
64015 Roo.LoadMask.prototype = {
64016     /**
64017      * @cfg {Boolean} removeMask
64018      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64019      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64020      */
64021     removeMask : false,
64022     /**
64023      * @cfg {String} msg
64024      * The text to display in a centered loading message box (defaults to 'Loading...')
64025      */
64026     msg : 'Loading...',
64027     /**
64028      * @cfg {String} msgCls
64029      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64030      */
64031     msgCls : 'x-mask-loading',
64032
64033     /**
64034      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64035      * @type Boolean
64036      */
64037     disabled: false,
64038
64039     /**
64040      * Disables the mask to prevent it from being displayed
64041      */
64042     disable : function(){
64043        this.disabled = true;
64044     },
64045
64046     /**
64047      * Enables the mask so that it can be displayed
64048      */
64049     enable : function(){
64050         this.disabled = false;
64051     },
64052     
64053     onLoadException : function()
64054     {
64055         Roo.log(arguments);
64056         
64057         if (typeof(arguments[3]) != 'undefined') {
64058             Roo.MessageBox.alert("Error loading",arguments[3]);
64059         } 
64060         /*
64061         try {
64062             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64063                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64064             }   
64065         } catch(e) {
64066             
64067         }
64068         */
64069     
64070         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64071     },
64072     // private
64073     onLoad : function()
64074     {
64075         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64076     },
64077
64078     // private
64079     onBeforeLoad : function(){
64080         if(!this.disabled){
64081             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64082         }
64083     },
64084
64085     // private
64086     destroy : function(){
64087         if(this.store){
64088             this.store.un('beforeload', this.onBeforeLoad, this);
64089             this.store.un('load', this.onLoad, this);
64090             this.store.un('loadexception', this.onLoadException, this);
64091         }else{
64092             var um = this.el.getUpdateManager();
64093             um.un('beforeupdate', this.onBeforeLoad, this);
64094             um.un('update', this.onLoad, this);
64095             um.un('failure', this.onLoad, this);
64096         }
64097     }
64098 };/*
64099  * Based on:
64100  * Ext JS Library 1.1.1
64101  * Copyright(c) 2006-2007, Ext JS, LLC.
64102  *
64103  * Originally Released Under LGPL - original licence link has changed is not relivant.
64104  *
64105  * Fork - LGPL
64106  * <script type="text/javascript">
64107  */
64108
64109
64110 /**
64111  * @class Roo.XTemplate
64112  * @extends Roo.Template
64113  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64114 <pre><code>
64115 var t = new Roo.XTemplate(
64116         '&lt;select name="{name}"&gt;',
64117                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64118         '&lt;/select&gt;'
64119 );
64120  
64121 // then append, applying the master template values
64122  </code></pre>
64123  *
64124  * Supported features:
64125  *
64126  *  Tags:
64127
64128 <pre><code>
64129       {a_variable} - output encoded.
64130       {a_variable.format:("Y-m-d")} - call a method on the variable
64131       {a_variable:raw} - unencoded output
64132       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64133       {a_variable:this.method_on_template(...)} - call a method on the template object.
64134  
64135 </code></pre>
64136  *  The tpl tag:
64137 <pre><code>
64138         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64139         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64140         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64141         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64142   
64143         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64144         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64145 </code></pre>
64146  *      
64147  */
64148 Roo.XTemplate = function()
64149 {
64150     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64151     if (this.html) {
64152         this.compile();
64153     }
64154 };
64155
64156
64157 Roo.extend(Roo.XTemplate, Roo.Template, {
64158
64159     /**
64160      * The various sub templates
64161      */
64162     tpls : false,
64163     /**
64164      *
64165      * basic tag replacing syntax
64166      * WORD:WORD()
64167      *
64168      * // you can fake an object call by doing this
64169      *  x.t:(test,tesT) 
64170      * 
64171      */
64172     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64173
64174     /**
64175      * compile the template
64176      *
64177      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64178      *
64179      */
64180     compile: function()
64181     {
64182         var s = this.html;
64183      
64184         s = ['<tpl>', s, '</tpl>'].join('');
64185     
64186         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64187             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64188             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64189             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64190             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64191             m,
64192             id     = 0,
64193             tpls   = [];
64194     
64195         while(true == !!(m = s.match(re))){
64196             var forMatch   = m[0].match(nameRe),
64197                 ifMatch   = m[0].match(ifRe),
64198                 execMatch   = m[0].match(execRe),
64199                 namedMatch   = m[0].match(namedRe),
64200                 
64201                 exp  = null, 
64202                 fn   = null,
64203                 exec = null,
64204                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64205                 
64206             if (ifMatch) {
64207                 // if - puts fn into test..
64208                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64209                 if(exp){
64210                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64211                 }
64212             }
64213             
64214             if (execMatch) {
64215                 // exec - calls a function... returns empty if true is  returned.
64216                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64217                 if(exp){
64218                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64219                 }
64220             }
64221             
64222             
64223             if (name) {
64224                 // for = 
64225                 switch(name){
64226                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64227                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64228                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64229                 }
64230             }
64231             var uid = namedMatch ? namedMatch[1] : id;
64232             
64233             
64234             tpls.push({
64235                 id:     namedMatch ? namedMatch[1] : id,
64236                 target: name,
64237                 exec:   exec,
64238                 test:   fn,
64239                 body:   m[1] || ''
64240             });
64241             if (namedMatch) {
64242                 s = s.replace(m[0], '');
64243             } else { 
64244                 s = s.replace(m[0], '{xtpl'+ id + '}');
64245             }
64246             ++id;
64247         }
64248         this.tpls = [];
64249         for(var i = tpls.length-1; i >= 0; --i){
64250             this.compileTpl(tpls[i]);
64251             this.tpls[tpls[i].id] = tpls[i];
64252         }
64253         this.master = tpls[tpls.length-1];
64254         return this;
64255     },
64256     /**
64257      * same as applyTemplate, except it's done to one of the subTemplates
64258      * when using named templates, you can do:
64259      *
64260      * var str = pl.applySubTemplate('your-name', values);
64261      *
64262      * 
64263      * @param {Number} id of the template
64264      * @param {Object} values to apply to template
64265      * @param {Object} parent (normaly the instance of this object)
64266      */
64267     applySubTemplate : function(id, values, parent)
64268     {
64269         
64270         
64271         var t = this.tpls[id];
64272         
64273         
64274         try { 
64275             if(t.test && !t.test.call(this, values, parent)){
64276                 return '';
64277             }
64278         } catch(e) {
64279             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64280             Roo.log(e.toString());
64281             Roo.log(t.test);
64282             return ''
64283         }
64284         try { 
64285             
64286             if(t.exec && t.exec.call(this, values, parent)){
64287                 return '';
64288             }
64289         } catch(e) {
64290             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64291             Roo.log(e.toString());
64292             Roo.log(t.exec);
64293             return ''
64294         }
64295         try {
64296             var vs = t.target ? t.target.call(this, values, parent) : values;
64297             parent = t.target ? values : parent;
64298             if(t.target && vs instanceof Array){
64299                 var buf = [];
64300                 for(var i = 0, len = vs.length; i < len; i++){
64301                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64302                 }
64303                 return buf.join('');
64304             }
64305             return t.compiled.call(this, vs, parent);
64306         } catch (e) {
64307             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64308             Roo.log(e.toString());
64309             Roo.log(t.compiled);
64310             return '';
64311         }
64312     },
64313
64314     compileTpl : function(tpl)
64315     {
64316         var fm = Roo.util.Format;
64317         var useF = this.disableFormats !== true;
64318         var sep = Roo.isGecko ? "+" : ",";
64319         var undef = function(str) {
64320             Roo.log("Property not found :"  + str);
64321             return '';
64322         };
64323         
64324         var fn = function(m, name, format, args)
64325         {
64326             //Roo.log(arguments);
64327             args = args ? args.replace(/\\'/g,"'") : args;
64328             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64329             if (typeof(format) == 'undefined') {
64330                 format= 'htmlEncode';
64331             }
64332             if (format == 'raw' ) {
64333                 format = false;
64334             }
64335             
64336             if(name.substr(0, 4) == 'xtpl'){
64337                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64338             }
64339             
64340             // build an array of options to determine if value is undefined..
64341             
64342             // basically get 'xxxx.yyyy' then do
64343             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64344             //    (function () { Roo.log("Property not found"); return ''; })() :
64345             //    ......
64346             
64347             var udef_ar = [];
64348             var lookfor = '';
64349             Roo.each(name.split('.'), function(st) {
64350                 lookfor += (lookfor.length ? '.': '') + st;
64351                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64352             });
64353             
64354             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64355             
64356             
64357             if(format && useF){
64358                 
64359                 args = args ? ',' + args : "";
64360                  
64361                 if(format.substr(0, 5) != "this."){
64362                     format = "fm." + format + '(';
64363                 }else{
64364                     format = 'this.call("'+ format.substr(5) + '", ';
64365                     args = ", values";
64366                 }
64367                 
64368                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64369             }
64370              
64371             if (args.length) {
64372                 // called with xxyx.yuu:(test,test)
64373                 // change to ()
64374                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64375             }
64376             // raw.. - :raw modifier..
64377             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64378             
64379         };
64380         var body;
64381         // branched to use + in gecko and [].join() in others
64382         if(Roo.isGecko){
64383             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64384                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64385                     "';};};";
64386         }else{
64387             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64388             body.push(tpl.body.replace(/(\r\n|\n)/g,
64389                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64390             body.push("'].join('');};};");
64391             body = body.join('');
64392         }
64393         
64394         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64395        
64396         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64397         eval(body);
64398         
64399         return this;
64400     },
64401
64402     applyTemplate : function(values){
64403         return this.master.compiled.call(this, values, {});
64404         //var s = this.subs;
64405     },
64406
64407     apply : function(){
64408         return this.applyTemplate.apply(this, arguments);
64409     }
64410
64411  });
64412
64413 Roo.XTemplate.from = function(el){
64414     el = Roo.getDom(el);
64415     return new Roo.XTemplate(el.value || el.innerHTML);
64416 };