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);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(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 = 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      * @cfg {Date/String} minValue
41585      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41586      * valid format (defaults to null).
41587      */
41588     minValue : null,
41589     /**
41590      * @cfg {Date/String} maxValue
41591      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41592      * valid format (defaults to null).
41593      */
41594     maxValue : null,
41595     /**
41596      * @cfg {String} minText
41597      * The error text to display when the date in the cell is before minValue (defaults to
41598      * 'The date in this field must be after {minValue}').
41599      */
41600     minText : "The date in this field must be equal to or after {0}",
41601     /**
41602      * @cfg {String} maxText
41603      * The error text to display when the date in the cell is after maxValue (defaults to
41604      * 'The date in this field must be before {maxValue}').
41605      */
41606     maxText : "The date in this field must be equal to or before {0}",
41607     /**
41608      * @cfg {String} invalidText
41609      * The error text to display when the date in the field is invalid (defaults to
41610      * '{value} is not a valid date - it must be in the format {format}').
41611      */
41612     invalidText : "{0} is not a valid date - it must be in the format {1}",
41613     /**
41614      * @cfg {String} triggerClass
41615      * An additional CSS class used to style the trigger button.  The trigger will always get the
41616      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41617      * which displays a calendar icon).
41618      */
41619     triggerClass : 'x-form-date-trigger',
41620     
41621
41622     /**
41623      * @cfg {Boolean} useIso
41624      * if enabled, then the date field will use a hidden field to store the 
41625      * real value as iso formated date. default (false)
41626      */ 
41627     useIso : false,
41628     /**
41629      * @cfg {String/Object} autoCreate
41630      * A DomHelper element spec, or true for a default element spec (defaults to
41631      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41632      */ 
41633     // private
41634     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41635     
41636     // private
41637     hiddenField: false,
41638     
41639     onRender : function(ct, position)
41640     {
41641         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41642         if (this.useIso) {
41643             //this.el.dom.removeAttribute('name'); 
41644             Roo.log("Changing name?");
41645             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41646             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41647                     'before', true);
41648             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41649             // prevent input submission
41650             this.hiddenName = this.name;
41651         }
41652             
41653             
41654     },
41655     
41656     // private
41657     validateValue : function(value)
41658     {
41659         value = this.formatDate(value);
41660         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41661             Roo.log('super failed');
41662             return false;
41663         }
41664         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41665              return true;
41666         }
41667         var svalue = value;
41668         value = this.parseDate(value);
41669         if(!value){
41670             Roo.log('parse date failed' + svalue);
41671             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41672             return false;
41673         }
41674         var time = value.getTime();
41675         if(this.minValue && time < this.minValue.getTime()){
41676             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41677             return false;
41678         }
41679         if(this.maxValue && time > this.maxValue.getTime()){
41680             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41681             return false;
41682         }
41683         if(this.disabledDays){
41684             var day = value.getDay();
41685             for(var i = 0; i < this.disabledDays.length; i++) {
41686                 if(day === this.disabledDays[i]){
41687                     this.markInvalid(this.disabledDaysText);
41688                     return false;
41689                 }
41690             }
41691         }
41692         var fvalue = this.formatDate(value);
41693         if(this.ddMatch && this.ddMatch.test(fvalue)){
41694             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41695             return false;
41696         }
41697         return true;
41698     },
41699
41700     // private
41701     // Provides logic to override the default TriggerField.validateBlur which just returns true
41702     validateBlur : function(){
41703         return !this.menu || !this.menu.isVisible();
41704     },
41705     
41706     getName: function()
41707     {
41708         // returns hidden if it's set..
41709         if (!this.rendered) {return ''};
41710         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41711         
41712     },
41713
41714     /**
41715      * Returns the current date value of the date field.
41716      * @return {Date} The date value
41717      */
41718     getValue : function(){
41719         
41720         return  this.hiddenField ?
41721                 this.hiddenField.value :
41722                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41723     },
41724
41725     /**
41726      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41727      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41728      * (the default format used is "m/d/y").
41729      * <br />Usage:
41730      * <pre><code>
41731 //All of these calls set the same date value (May 4, 2006)
41732
41733 //Pass a date object:
41734 var dt = new Date('5/4/06');
41735 dateField.setValue(dt);
41736
41737 //Pass a date string (default format):
41738 dateField.setValue('5/4/06');
41739
41740 //Pass a date string (custom format):
41741 dateField.format = 'Y-m-d';
41742 dateField.setValue('2006-5-4');
41743 </code></pre>
41744      * @param {String/Date} date The date or valid date string
41745      */
41746     setValue : function(date){
41747         if (this.hiddenField) {
41748             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41749         }
41750         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41751         // make sure the value field is always stored as a date..
41752         this.value = this.parseDate(date);
41753         
41754         
41755     },
41756
41757     // private
41758     parseDate : function(value){
41759         if(!value || value instanceof Date){
41760             return value;
41761         }
41762         var v = Date.parseDate(value, this.format);
41763          if (!v && this.useIso) {
41764             v = Date.parseDate(value, 'Y-m-d');
41765         }
41766         if(!v && this.altFormats){
41767             if(!this.altFormatsArray){
41768                 this.altFormatsArray = this.altFormats.split("|");
41769             }
41770             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41771                 v = Date.parseDate(value, this.altFormatsArray[i]);
41772             }
41773         }
41774                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41775                         v = '';
41776                 }
41777         return v;
41778     },
41779
41780     // private
41781     formatDate : function(date, fmt){
41782         return (!date || !(date instanceof Date)) ?
41783                date : date.dateFormat(fmt || this.format);
41784     },
41785
41786     // private
41787     menuListeners : {
41788         select: function(m, d){
41789             
41790             this.setValue(d);
41791             this.fireEvent('select', this, d);
41792         },
41793         show : function(){ // retain focus styling
41794             this.onFocus();
41795         },
41796         hide : function(){
41797             this.focus.defer(10, this);
41798             var ml = this.menuListeners;
41799             this.menu.un("select", ml.select,  this);
41800             this.menu.un("show", ml.show,  this);
41801             this.menu.un("hide", ml.hide,  this);
41802         }
41803     },
41804
41805     // private
41806     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41807     onTriggerClick : function(){
41808         if(this.disabled){
41809             return;
41810         }
41811         if(this.menu == null){
41812             this.menu = new Roo.menu.DateMenu();
41813         }
41814         Roo.apply(this.menu.picker,  {
41815             showClear: this.allowBlank,
41816             minDate : this.minValue,
41817             maxDate : this.maxValue,
41818             disabledDatesRE : this.ddMatch,
41819             disabledDatesText : this.disabledDatesText,
41820             disabledDays : this.disabledDays,
41821             disabledDaysText : this.disabledDaysText,
41822             format : this.useIso ? 'Y-m-d' : this.format,
41823             minText : String.format(this.minText, this.formatDate(this.minValue)),
41824             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41825         });
41826         this.menu.on(Roo.apply({}, this.menuListeners, {
41827             scope:this
41828         }));
41829         this.menu.picker.setValue(this.getValue() || new Date());
41830         this.menu.show(this.el, "tl-bl?");
41831     },
41832
41833     beforeBlur : function(){
41834         var v = this.parseDate(this.getRawValue());
41835         if(v){
41836             this.setValue(v);
41837         }
41838     },
41839
41840     /*@
41841      * overide
41842      * 
41843      */
41844     isDirty : function() {
41845         if(this.disabled) {
41846             return false;
41847         }
41848         
41849         if(typeof(this.startValue) === 'undefined'){
41850             return false;
41851         }
41852         
41853         return String(this.getValue()) !== String(this.startValue);
41854         
41855     },
41856     // @overide
41857     cleanLeadingSpace : function(e)
41858     {
41859        return;
41860     }
41861     
41862 });/*
41863  * Based on:
41864  * Ext JS Library 1.1.1
41865  * Copyright(c) 2006-2007, Ext JS, LLC.
41866  *
41867  * Originally Released Under LGPL - original licence link has changed is not relivant.
41868  *
41869  * Fork - LGPL
41870  * <script type="text/javascript">
41871  */
41872  
41873 /**
41874  * @class Roo.form.MonthField
41875  * @extends Roo.form.TriggerField
41876  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41877 * @constructor
41878 * Create a new MonthField
41879 * @param {Object} config
41880  */
41881 Roo.form.MonthField = function(config){
41882     
41883     Roo.form.MonthField.superclass.constructor.call(this, config);
41884     
41885       this.addEvents({
41886          
41887         /**
41888          * @event select
41889          * Fires when a date is selected
41890              * @param {Roo.form.MonthFieeld} combo This combo box
41891              * @param {Date} date The date selected
41892              */
41893         'select' : true
41894          
41895     });
41896     
41897     
41898     if(typeof this.minValue == "string") {
41899         this.minValue = this.parseDate(this.minValue);
41900     }
41901     if(typeof this.maxValue == "string") {
41902         this.maxValue = this.parseDate(this.maxValue);
41903     }
41904     this.ddMatch = null;
41905     if(this.disabledDates){
41906         var dd = this.disabledDates;
41907         var re = "(?:";
41908         for(var i = 0; i < dd.length; i++){
41909             re += dd[i];
41910             if(i != dd.length-1) {
41911                 re += "|";
41912             }
41913         }
41914         this.ddMatch = new RegExp(re + ")");
41915     }
41916 };
41917
41918 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41919     /**
41920      * @cfg {String} format
41921      * The default date format string which can be overriden for localization support.  The format must be
41922      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41923      */
41924     format : "M Y",
41925     /**
41926      * @cfg {String} altFormats
41927      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41928      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41929      */
41930     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41931     /**
41932      * @cfg {Array} disabledDays
41933      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41934      */
41935     disabledDays : [0,1,2,3,4,5,6],
41936     /**
41937      * @cfg {String} disabledDaysText
41938      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41939      */
41940     disabledDaysText : "Disabled",
41941     /**
41942      * @cfg {Array} disabledDates
41943      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41944      * expression so they are very powerful. Some examples:
41945      * <ul>
41946      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41947      * <li>["03/08", "09/16"] would disable those days for every year</li>
41948      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41949      * <li>["03/../2006"] would disable every day in March 2006</li>
41950      * <li>["^03"] would disable every day in every March</li>
41951      * </ul>
41952      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41953      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41954      */
41955     disabledDates : null,
41956     /**
41957      * @cfg {String} disabledDatesText
41958      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41959      */
41960     disabledDatesText : "Disabled",
41961     /**
41962      * @cfg {Date/String} minValue
41963      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41964      * valid format (defaults to null).
41965      */
41966     minValue : null,
41967     /**
41968      * @cfg {Date/String} maxValue
41969      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41970      * valid format (defaults to null).
41971      */
41972     maxValue : null,
41973     /**
41974      * @cfg {String} minText
41975      * The error text to display when the date in the cell is before minValue (defaults to
41976      * 'The date in this field must be after {minValue}').
41977      */
41978     minText : "The date in this field must be equal to or after {0}",
41979     /**
41980      * @cfg {String} maxTextf
41981      * The error text to display when the date in the cell is after maxValue (defaults to
41982      * 'The date in this field must be before {maxValue}').
41983      */
41984     maxText : "The date in this field must be equal to or before {0}",
41985     /**
41986      * @cfg {String} invalidText
41987      * The error text to display when the date in the field is invalid (defaults to
41988      * '{value} is not a valid date - it must be in the format {format}').
41989      */
41990     invalidText : "{0} is not a valid date - it must be in the format {1}",
41991     /**
41992      * @cfg {String} triggerClass
41993      * An additional CSS class used to style the trigger button.  The trigger will always get the
41994      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41995      * which displays a calendar icon).
41996      */
41997     triggerClass : 'x-form-date-trigger',
41998     
41999
42000     /**
42001      * @cfg {Boolean} useIso
42002      * if enabled, then the date field will use a hidden field to store the 
42003      * real value as iso formated date. default (true)
42004      */ 
42005     useIso : true,
42006     /**
42007      * @cfg {String/Object} autoCreate
42008      * A DomHelper element spec, or true for a default element spec (defaults to
42009      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42010      */ 
42011     // private
42012     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42013     
42014     // private
42015     hiddenField: false,
42016     
42017     hideMonthPicker : false,
42018     
42019     onRender : function(ct, position)
42020     {
42021         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42022         if (this.useIso) {
42023             this.el.dom.removeAttribute('name'); 
42024             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42025                     'before', true);
42026             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42027             // prevent input submission
42028             this.hiddenName = this.name;
42029         }
42030             
42031             
42032     },
42033     
42034     // private
42035     validateValue : function(value)
42036     {
42037         value = this.formatDate(value);
42038         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42039             return false;
42040         }
42041         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42042              return true;
42043         }
42044         var svalue = value;
42045         value = this.parseDate(value);
42046         if(!value){
42047             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42048             return false;
42049         }
42050         var time = value.getTime();
42051         if(this.minValue && time < this.minValue.getTime()){
42052             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42053             return false;
42054         }
42055         if(this.maxValue && time > this.maxValue.getTime()){
42056             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42057             return false;
42058         }
42059         /*if(this.disabledDays){
42060             var day = value.getDay();
42061             for(var i = 0; i < this.disabledDays.length; i++) {
42062                 if(day === this.disabledDays[i]){
42063                     this.markInvalid(this.disabledDaysText);
42064                     return false;
42065                 }
42066             }
42067         }
42068         */
42069         var fvalue = this.formatDate(value);
42070         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42071             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42072             return false;
42073         }
42074         */
42075         return true;
42076     },
42077
42078     // private
42079     // Provides logic to override the default TriggerField.validateBlur which just returns true
42080     validateBlur : function(){
42081         return !this.menu || !this.menu.isVisible();
42082     },
42083
42084     /**
42085      * Returns the current date value of the date field.
42086      * @return {Date} The date value
42087      */
42088     getValue : function(){
42089         
42090         
42091         
42092         return  this.hiddenField ?
42093                 this.hiddenField.value :
42094                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42095     },
42096
42097     /**
42098      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42099      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42100      * (the default format used is "m/d/y").
42101      * <br />Usage:
42102      * <pre><code>
42103 //All of these calls set the same date value (May 4, 2006)
42104
42105 //Pass a date object:
42106 var dt = new Date('5/4/06');
42107 monthField.setValue(dt);
42108
42109 //Pass a date string (default format):
42110 monthField.setValue('5/4/06');
42111
42112 //Pass a date string (custom format):
42113 monthField.format = 'Y-m-d';
42114 monthField.setValue('2006-5-4');
42115 </code></pre>
42116      * @param {String/Date} date The date or valid date string
42117      */
42118     setValue : function(date){
42119         Roo.log('month setValue' + date);
42120         // can only be first of month..
42121         
42122         var val = this.parseDate(date);
42123         
42124         if (this.hiddenField) {
42125             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42126         }
42127         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42128         this.value = this.parseDate(date);
42129     },
42130
42131     // private
42132     parseDate : function(value){
42133         if(!value || value instanceof Date){
42134             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42135             return value;
42136         }
42137         var v = Date.parseDate(value, this.format);
42138         if (!v && this.useIso) {
42139             v = Date.parseDate(value, 'Y-m-d');
42140         }
42141         if (v) {
42142             // 
42143             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42144         }
42145         
42146         
42147         if(!v && this.altFormats){
42148             if(!this.altFormatsArray){
42149                 this.altFormatsArray = this.altFormats.split("|");
42150             }
42151             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42152                 v = Date.parseDate(value, this.altFormatsArray[i]);
42153             }
42154         }
42155         return v;
42156     },
42157
42158     // private
42159     formatDate : function(date, fmt){
42160         return (!date || !(date instanceof Date)) ?
42161                date : date.dateFormat(fmt || this.format);
42162     },
42163
42164     // private
42165     menuListeners : {
42166         select: function(m, d){
42167             this.setValue(d);
42168             this.fireEvent('select', this, d);
42169         },
42170         show : function(){ // retain focus styling
42171             this.onFocus();
42172         },
42173         hide : function(){
42174             this.focus.defer(10, this);
42175             var ml = this.menuListeners;
42176             this.menu.un("select", ml.select,  this);
42177             this.menu.un("show", ml.show,  this);
42178             this.menu.un("hide", ml.hide,  this);
42179         }
42180     },
42181     // private
42182     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42183     onTriggerClick : function(){
42184         if(this.disabled){
42185             return;
42186         }
42187         if(this.menu == null){
42188             this.menu = new Roo.menu.DateMenu();
42189            
42190         }
42191         
42192         Roo.apply(this.menu.picker,  {
42193             
42194             showClear: this.allowBlank,
42195             minDate : this.minValue,
42196             maxDate : this.maxValue,
42197             disabledDatesRE : this.ddMatch,
42198             disabledDatesText : this.disabledDatesText,
42199             
42200             format : this.useIso ? 'Y-m-d' : this.format,
42201             minText : String.format(this.minText, this.formatDate(this.minValue)),
42202             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42203             
42204         });
42205          this.menu.on(Roo.apply({}, this.menuListeners, {
42206             scope:this
42207         }));
42208        
42209         
42210         var m = this.menu;
42211         var p = m.picker;
42212         
42213         // hide month picker get's called when we called by 'before hide';
42214         
42215         var ignorehide = true;
42216         p.hideMonthPicker  = function(disableAnim){
42217             if (ignorehide) {
42218                 return;
42219             }
42220              if(this.monthPicker){
42221                 Roo.log("hideMonthPicker called");
42222                 if(disableAnim === true){
42223                     this.monthPicker.hide();
42224                 }else{
42225                     this.monthPicker.slideOut('t', {duration:.2});
42226                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42227                     p.fireEvent("select", this, this.value);
42228                     m.hide();
42229                 }
42230             }
42231         }
42232         
42233         Roo.log('picker set value');
42234         Roo.log(this.getValue());
42235         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42236         m.show(this.el, 'tl-bl?');
42237         ignorehide  = false;
42238         // this will trigger hideMonthPicker..
42239         
42240         
42241         // hidden the day picker
42242         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42243         
42244         
42245         
42246       
42247         
42248         p.showMonthPicker.defer(100, p);
42249     
42250         
42251        
42252     },
42253
42254     beforeBlur : function(){
42255         var v = this.parseDate(this.getRawValue());
42256         if(v){
42257             this.setValue(v);
42258         }
42259     }
42260
42261     /** @cfg {Boolean} grow @hide */
42262     /** @cfg {Number} growMin @hide */
42263     /** @cfg {Number} growMax @hide */
42264     /**
42265      * @hide
42266      * @method autoSize
42267      */
42268 });/*
42269  * Based on:
42270  * Ext JS Library 1.1.1
42271  * Copyright(c) 2006-2007, Ext JS, LLC.
42272  *
42273  * Originally Released Under LGPL - original licence link has changed is not relivant.
42274  *
42275  * Fork - LGPL
42276  * <script type="text/javascript">
42277  */
42278  
42279
42280 /**
42281  * @class Roo.form.ComboBox
42282  * @extends Roo.form.TriggerField
42283  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42284  * @constructor
42285  * Create a new ComboBox.
42286  * @param {Object} config Configuration options
42287  */
42288 Roo.form.ComboBox = function(config){
42289     Roo.form.ComboBox.superclass.constructor.call(this, config);
42290     this.addEvents({
42291         /**
42292          * @event expand
42293          * Fires when the dropdown list is expanded
42294              * @param {Roo.form.ComboBox} combo This combo box
42295              */
42296         'expand' : true,
42297         /**
42298          * @event collapse
42299          * Fires when the dropdown list is collapsed
42300              * @param {Roo.form.ComboBox} combo This combo box
42301              */
42302         'collapse' : true,
42303         /**
42304          * @event beforeselect
42305          * Fires before a list item is selected. Return false to cancel the selection.
42306              * @param {Roo.form.ComboBox} combo This combo box
42307              * @param {Roo.data.Record} record The data record returned from the underlying store
42308              * @param {Number} index The index of the selected item in the dropdown list
42309              */
42310         'beforeselect' : true,
42311         /**
42312          * @event select
42313          * Fires when a list item is selected
42314              * @param {Roo.form.ComboBox} combo This combo box
42315              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42316              * @param {Number} index The index of the selected item in the dropdown list
42317              */
42318         'select' : true,
42319         /**
42320          * @event beforequery
42321          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42322          * The event object passed has these properties:
42323              * @param {Roo.form.ComboBox} combo This combo box
42324              * @param {String} query The query
42325              * @param {Boolean} forceAll true to force "all" query
42326              * @param {Boolean} cancel true to cancel the query
42327              * @param {Object} e The query event object
42328              */
42329         'beforequery': true,
42330          /**
42331          * @event add
42332          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42333              * @param {Roo.form.ComboBox} combo This combo box
42334              */
42335         'add' : true,
42336         /**
42337          * @event edit
42338          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42339              * @param {Roo.form.ComboBox} combo This combo box
42340              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42341              */
42342         'edit' : true
42343         
42344         
42345     });
42346     if(this.transform){
42347         this.allowDomMove = false;
42348         var s = Roo.getDom(this.transform);
42349         if(!this.hiddenName){
42350             this.hiddenName = s.name;
42351         }
42352         if(!this.store){
42353             this.mode = 'local';
42354             var d = [], opts = s.options;
42355             for(var i = 0, len = opts.length;i < len; i++){
42356                 var o = opts[i];
42357                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42358                 if(o.selected) {
42359                     this.value = value;
42360                 }
42361                 d.push([value, o.text]);
42362             }
42363             this.store = new Roo.data.SimpleStore({
42364                 'id': 0,
42365                 fields: ['value', 'text'],
42366                 data : d
42367             });
42368             this.valueField = 'value';
42369             this.displayField = 'text';
42370         }
42371         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42372         if(!this.lazyRender){
42373             this.target = true;
42374             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42375             s.parentNode.removeChild(s); // remove it
42376             this.render(this.el.parentNode);
42377         }else{
42378             s.parentNode.removeChild(s); // remove it
42379         }
42380
42381     }
42382     if (this.store) {
42383         this.store = Roo.factory(this.store, Roo.data);
42384     }
42385     
42386     this.selectedIndex = -1;
42387     if(this.mode == 'local'){
42388         if(config.queryDelay === undefined){
42389             this.queryDelay = 10;
42390         }
42391         if(config.minChars === undefined){
42392             this.minChars = 0;
42393         }
42394     }
42395 };
42396
42397 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42398     /**
42399      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42400      */
42401     /**
42402      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42403      * rendering into an Roo.Editor, defaults to false)
42404      */
42405     /**
42406      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42407      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42408      */
42409     /**
42410      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42411      */
42412     /**
42413      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42414      * the dropdown list (defaults to undefined, with no header element)
42415      */
42416
42417      /**
42418      * @cfg {String/Roo.Template} tpl The template to use to render the output
42419      */
42420      
42421     // private
42422     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42423     /**
42424      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42425      */
42426     listWidth: undefined,
42427     /**
42428      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42429      * mode = 'remote' or 'text' if mode = 'local')
42430      */
42431     displayField: undefined,
42432     /**
42433      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42434      * mode = 'remote' or 'value' if mode = 'local'). 
42435      * Note: use of a valueField requires the user make a selection
42436      * in order for a value to be mapped.
42437      */
42438     valueField: undefined,
42439     
42440     
42441     /**
42442      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42443      * field's data value (defaults to the underlying DOM element's name)
42444      */
42445     hiddenName: undefined,
42446     /**
42447      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42448      */
42449     listClass: '',
42450     /**
42451      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42452      */
42453     selectedClass: 'x-combo-selected',
42454     /**
42455      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42456      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42457      * which displays a downward arrow icon).
42458      */
42459     triggerClass : 'x-form-arrow-trigger',
42460     /**
42461      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42462      */
42463     shadow:'sides',
42464     /**
42465      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42466      * anchor positions (defaults to 'tl-bl')
42467      */
42468     listAlign: 'tl-bl?',
42469     /**
42470      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42471      */
42472     maxHeight: 300,
42473     /**
42474      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42475      * query specified by the allQuery config option (defaults to 'query')
42476      */
42477     triggerAction: 'query',
42478     /**
42479      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42480      * (defaults to 4, does not apply if editable = false)
42481      */
42482     minChars : 4,
42483     /**
42484      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42485      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42486      */
42487     typeAhead: false,
42488     /**
42489      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42490      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42491      */
42492     queryDelay: 500,
42493     /**
42494      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42495      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42496      */
42497     pageSize: 0,
42498     /**
42499      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42500      * when editable = true (defaults to false)
42501      */
42502     selectOnFocus:false,
42503     /**
42504      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42505      */
42506     queryParam: 'query',
42507     /**
42508      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42509      * when mode = 'remote' (defaults to 'Loading...')
42510      */
42511     loadingText: 'Loading...',
42512     /**
42513      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42514      */
42515     resizable: false,
42516     /**
42517      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42518      */
42519     handleHeight : 8,
42520     /**
42521      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42522      * traditional select (defaults to true)
42523      */
42524     editable: true,
42525     /**
42526      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42527      */
42528     allQuery: '',
42529     /**
42530      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42531      */
42532     mode: 'remote',
42533     /**
42534      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42535      * listWidth has a higher value)
42536      */
42537     minListWidth : 70,
42538     /**
42539      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42540      * allow the user to set arbitrary text into the field (defaults to false)
42541      */
42542     forceSelection:false,
42543     /**
42544      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42545      * if typeAhead = true (defaults to 250)
42546      */
42547     typeAheadDelay : 250,
42548     /**
42549      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42550      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42551      */
42552     valueNotFoundText : undefined,
42553     /**
42554      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42555      */
42556     blockFocus : false,
42557     
42558     /**
42559      * @cfg {Boolean} disableClear Disable showing of clear button.
42560      */
42561     disableClear : false,
42562     /**
42563      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42564      */
42565     alwaysQuery : false,
42566     
42567     //private
42568     addicon : false,
42569     editicon: false,
42570     
42571     // element that contains real text value.. (when hidden is used..)
42572      
42573     // private
42574     onRender : function(ct, position)
42575     {
42576         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42577         
42578         if(this.hiddenName){
42579             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42580                     'before', true);
42581             this.hiddenField.value =
42582                 this.hiddenValue !== undefined ? this.hiddenValue :
42583                 this.value !== undefined ? this.value : '';
42584
42585             // prevent input submission
42586             this.el.dom.removeAttribute('name');
42587              
42588              
42589         }
42590         
42591         if(Roo.isGecko){
42592             this.el.dom.setAttribute('autocomplete', 'off');
42593         }
42594
42595         var cls = 'x-combo-list';
42596
42597         this.list = new Roo.Layer({
42598             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42599         });
42600
42601         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42602         this.list.setWidth(lw);
42603         this.list.swallowEvent('mousewheel');
42604         this.assetHeight = 0;
42605
42606         if(this.title){
42607             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42608             this.assetHeight += this.header.getHeight();
42609         }
42610
42611         this.innerList = this.list.createChild({cls:cls+'-inner'});
42612         this.innerList.on('mouseover', this.onViewOver, this);
42613         this.innerList.on('mousemove', this.onViewMove, this);
42614         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42615         
42616         if(this.allowBlank && !this.pageSize && !this.disableClear){
42617             this.footer = this.list.createChild({cls:cls+'-ft'});
42618             this.pageTb = new Roo.Toolbar(this.footer);
42619            
42620         }
42621         if(this.pageSize){
42622             this.footer = this.list.createChild({cls:cls+'-ft'});
42623             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42624                     {pageSize: this.pageSize});
42625             
42626         }
42627         
42628         if (this.pageTb && this.allowBlank && !this.disableClear) {
42629             var _this = this;
42630             this.pageTb.add(new Roo.Toolbar.Fill(), {
42631                 cls: 'x-btn-icon x-btn-clear',
42632                 text: '&#160;',
42633                 handler: function()
42634                 {
42635                     _this.collapse();
42636                     _this.clearValue();
42637                     _this.onSelect(false, -1);
42638                 }
42639             });
42640         }
42641         if (this.footer) {
42642             this.assetHeight += this.footer.getHeight();
42643         }
42644         
42645
42646         if(!this.tpl){
42647             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42648         }
42649
42650         this.view = new Roo.View(this.innerList, this.tpl, {
42651             singleSelect:true,
42652             store: this.store,
42653             selectedClass: this.selectedClass
42654         });
42655
42656         this.view.on('click', this.onViewClick, this);
42657
42658         this.store.on('beforeload', this.onBeforeLoad, this);
42659         this.store.on('load', this.onLoad, this);
42660         this.store.on('loadexception', this.onLoadException, this);
42661
42662         if(this.resizable){
42663             this.resizer = new Roo.Resizable(this.list,  {
42664                pinned:true, handles:'se'
42665             });
42666             this.resizer.on('resize', function(r, w, h){
42667                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42668                 this.listWidth = w;
42669                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42670                 this.restrictHeight();
42671             }, this);
42672             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42673         }
42674         if(!this.editable){
42675             this.editable = true;
42676             this.setEditable(false);
42677         }  
42678         
42679         
42680         if (typeof(this.events.add.listeners) != 'undefined') {
42681             
42682             this.addicon = this.wrap.createChild(
42683                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42684        
42685             this.addicon.on('click', function(e) {
42686                 this.fireEvent('add', this);
42687             }, this);
42688         }
42689         if (typeof(this.events.edit.listeners) != 'undefined') {
42690             
42691             this.editicon = this.wrap.createChild(
42692                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42693             if (this.addicon) {
42694                 this.editicon.setStyle('margin-left', '40px');
42695             }
42696             this.editicon.on('click', function(e) {
42697                 
42698                 // we fire even  if inothing is selected..
42699                 this.fireEvent('edit', this, this.lastData );
42700                 
42701             }, this);
42702         }
42703         
42704         
42705         
42706     },
42707
42708     // private
42709     initEvents : function(){
42710         Roo.form.ComboBox.superclass.initEvents.call(this);
42711
42712         this.keyNav = new Roo.KeyNav(this.el, {
42713             "up" : function(e){
42714                 this.inKeyMode = true;
42715                 this.selectPrev();
42716             },
42717
42718             "down" : function(e){
42719                 if(!this.isExpanded()){
42720                     this.onTriggerClick();
42721                 }else{
42722                     this.inKeyMode = true;
42723                     this.selectNext();
42724                 }
42725             },
42726
42727             "enter" : function(e){
42728                 this.onViewClick();
42729                 //return true;
42730             },
42731
42732             "esc" : function(e){
42733                 this.collapse();
42734             },
42735
42736             "tab" : function(e){
42737                 this.onViewClick(false);
42738                 this.fireEvent("specialkey", this, e);
42739                 return true;
42740             },
42741
42742             scope : this,
42743
42744             doRelay : function(foo, bar, hname){
42745                 if(hname == 'down' || this.scope.isExpanded()){
42746                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42747                 }
42748                 return true;
42749             },
42750
42751             forceKeyDown: true
42752         });
42753         this.queryDelay = Math.max(this.queryDelay || 10,
42754                 this.mode == 'local' ? 10 : 250);
42755         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42756         if(this.typeAhead){
42757             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42758         }
42759         if(this.editable !== false){
42760             this.el.on("keyup", this.onKeyUp, this);
42761         }
42762         if(this.forceSelection){
42763             this.on('blur', this.doForce, this);
42764         }
42765     },
42766
42767     onDestroy : function(){
42768         if(this.view){
42769             this.view.setStore(null);
42770             this.view.el.removeAllListeners();
42771             this.view.el.remove();
42772             this.view.purgeListeners();
42773         }
42774         if(this.list){
42775             this.list.destroy();
42776         }
42777         if(this.store){
42778             this.store.un('beforeload', this.onBeforeLoad, this);
42779             this.store.un('load', this.onLoad, this);
42780             this.store.un('loadexception', this.onLoadException, this);
42781         }
42782         Roo.form.ComboBox.superclass.onDestroy.call(this);
42783     },
42784
42785     // private
42786     fireKey : function(e){
42787         if(e.isNavKeyPress() && !this.list.isVisible()){
42788             this.fireEvent("specialkey", this, e);
42789         }
42790     },
42791
42792     // private
42793     onResize: function(w, h){
42794         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42795         
42796         if(typeof w != 'number'){
42797             // we do not handle it!?!?
42798             return;
42799         }
42800         var tw = this.trigger.getWidth();
42801         tw += this.addicon ? this.addicon.getWidth() : 0;
42802         tw += this.editicon ? this.editicon.getWidth() : 0;
42803         var x = w - tw;
42804         this.el.setWidth( this.adjustWidth('input', x));
42805             
42806         this.trigger.setStyle('left', x+'px');
42807         
42808         if(this.list && this.listWidth === undefined){
42809             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42810             this.list.setWidth(lw);
42811             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42812         }
42813         
42814     
42815         
42816     },
42817
42818     /**
42819      * Allow or prevent the user from directly editing the field text.  If false is passed,
42820      * the user will only be able to select from the items defined in the dropdown list.  This method
42821      * is the runtime equivalent of setting the 'editable' config option at config time.
42822      * @param {Boolean} value True to allow the user to directly edit the field text
42823      */
42824     setEditable : function(value){
42825         if(value == this.editable){
42826             return;
42827         }
42828         this.editable = value;
42829         if(!value){
42830             this.el.dom.setAttribute('readOnly', true);
42831             this.el.on('mousedown', this.onTriggerClick,  this);
42832             this.el.addClass('x-combo-noedit');
42833         }else{
42834             this.el.dom.setAttribute('readOnly', false);
42835             this.el.un('mousedown', this.onTriggerClick,  this);
42836             this.el.removeClass('x-combo-noedit');
42837         }
42838     },
42839
42840     // private
42841     onBeforeLoad : function(){
42842         if(!this.hasFocus){
42843             return;
42844         }
42845         this.innerList.update(this.loadingText ?
42846                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42847         this.restrictHeight();
42848         this.selectedIndex = -1;
42849     },
42850
42851     // private
42852     onLoad : function(){
42853         if(!this.hasFocus){
42854             return;
42855         }
42856         if(this.store.getCount() > 0){
42857             this.expand();
42858             this.restrictHeight();
42859             if(this.lastQuery == this.allQuery){
42860                 if(this.editable){
42861                     this.el.dom.select();
42862                 }
42863                 if(!this.selectByValue(this.value, true)){
42864                     this.select(0, true);
42865                 }
42866             }else{
42867                 this.selectNext();
42868                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42869                     this.taTask.delay(this.typeAheadDelay);
42870                 }
42871             }
42872         }else{
42873             this.onEmptyResults();
42874         }
42875         //this.el.focus();
42876     },
42877     // private
42878     onLoadException : function()
42879     {
42880         this.collapse();
42881         Roo.log(this.store.reader.jsonData);
42882         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42883             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42884         }
42885         
42886         
42887     },
42888     // private
42889     onTypeAhead : function(){
42890         if(this.store.getCount() > 0){
42891             var r = this.store.getAt(0);
42892             var newValue = r.data[this.displayField];
42893             var len = newValue.length;
42894             var selStart = this.getRawValue().length;
42895             if(selStart != len){
42896                 this.setRawValue(newValue);
42897                 this.selectText(selStart, newValue.length);
42898             }
42899         }
42900     },
42901
42902     // private
42903     onSelect : function(record, index){
42904         if(this.fireEvent('beforeselect', this, record, index) !== false){
42905             this.setFromData(index > -1 ? record.data : false);
42906             this.collapse();
42907             this.fireEvent('select', this, record, index);
42908         }
42909     },
42910
42911     /**
42912      * Returns the currently selected field value or empty string if no value is set.
42913      * @return {String} value The selected value
42914      */
42915     getValue : function(){
42916         if(this.valueField){
42917             return typeof this.value != 'undefined' ? this.value : '';
42918         }
42919         return Roo.form.ComboBox.superclass.getValue.call(this);
42920     },
42921
42922     /**
42923      * Clears any text/value currently set in the field
42924      */
42925     clearValue : function(){
42926         if(this.hiddenField){
42927             this.hiddenField.value = '';
42928         }
42929         this.value = '';
42930         this.setRawValue('');
42931         this.lastSelectionText = '';
42932         
42933     },
42934
42935     /**
42936      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42937      * will be displayed in the field.  If the value does not match the data value of an existing item,
42938      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42939      * Otherwise the field will be blank (although the value will still be set).
42940      * @param {String} value The value to match
42941      */
42942     setValue : function(v){
42943         var text = v;
42944         if(this.valueField){
42945             var r = this.findRecord(this.valueField, v);
42946             if(r){
42947                 text = r.data[this.displayField];
42948             }else if(this.valueNotFoundText !== undefined){
42949                 text = this.valueNotFoundText;
42950             }
42951         }
42952         this.lastSelectionText = text;
42953         if(this.hiddenField){
42954             this.hiddenField.value = v;
42955         }
42956         Roo.form.ComboBox.superclass.setValue.call(this, text);
42957         this.value = v;
42958     },
42959     /**
42960      * @property {Object} the last set data for the element
42961      */
42962     
42963     lastData : false,
42964     /**
42965      * Sets the value of the field based on a object which is related to the record format for the store.
42966      * @param {Object} value the value to set as. or false on reset?
42967      */
42968     setFromData : function(o){
42969         var dv = ''; // display value
42970         var vv = ''; // value value..
42971         this.lastData = o;
42972         if (this.displayField) {
42973             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42974         } else {
42975             // this is an error condition!!!
42976             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42977         }
42978         
42979         if(this.valueField){
42980             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42981         }
42982         if(this.hiddenField){
42983             this.hiddenField.value = vv;
42984             
42985             this.lastSelectionText = dv;
42986             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42987             this.value = vv;
42988             return;
42989         }
42990         // no hidden field.. - we store the value in 'value', but still display
42991         // display field!!!!
42992         this.lastSelectionText = dv;
42993         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42994         this.value = vv;
42995         
42996         
42997     },
42998     // private
42999     reset : function(){
43000         // overridden so that last data is reset..
43001         this.setValue(this.resetValue);
43002         this.originalValue = this.getValue();
43003         this.clearInvalid();
43004         this.lastData = false;
43005         if (this.view) {
43006             this.view.clearSelections();
43007         }
43008     },
43009     // private
43010     findRecord : function(prop, value){
43011         var record;
43012         if(this.store.getCount() > 0){
43013             this.store.each(function(r){
43014                 if(r.data[prop] == value){
43015                     record = r;
43016                     return false;
43017                 }
43018                 return true;
43019             });
43020         }
43021         return record;
43022     },
43023     
43024     getName: function()
43025     {
43026         // returns hidden if it's set..
43027         if (!this.rendered) {return ''};
43028         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43029         
43030     },
43031     // private
43032     onViewMove : function(e, t){
43033         this.inKeyMode = false;
43034     },
43035
43036     // private
43037     onViewOver : function(e, t){
43038         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43039             return;
43040         }
43041         var item = this.view.findItemFromChild(t);
43042         if(item){
43043             var index = this.view.indexOf(item);
43044             this.select(index, false);
43045         }
43046     },
43047
43048     // private
43049     onViewClick : function(doFocus)
43050     {
43051         var index = this.view.getSelectedIndexes()[0];
43052         var r = this.store.getAt(index);
43053         if(r){
43054             this.onSelect(r, index);
43055         }
43056         if(doFocus !== false && !this.blockFocus){
43057             this.el.focus();
43058         }
43059     },
43060
43061     // private
43062     restrictHeight : function(){
43063         this.innerList.dom.style.height = '';
43064         var inner = this.innerList.dom;
43065         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43066         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43067         this.list.beginUpdate();
43068         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43069         this.list.alignTo(this.el, this.listAlign);
43070         this.list.endUpdate();
43071     },
43072
43073     // private
43074     onEmptyResults : function(){
43075         this.collapse();
43076     },
43077
43078     /**
43079      * Returns true if the dropdown list is expanded, else false.
43080      */
43081     isExpanded : function(){
43082         return this.list.isVisible();
43083     },
43084
43085     /**
43086      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43087      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43088      * @param {String} value The data value of the item to select
43089      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43090      * selected item if it is not currently in view (defaults to true)
43091      * @return {Boolean} True if the value matched an item in the list, else false
43092      */
43093     selectByValue : function(v, scrollIntoView){
43094         if(v !== undefined && v !== null){
43095             var r = this.findRecord(this.valueField || this.displayField, v);
43096             if(r){
43097                 this.select(this.store.indexOf(r), scrollIntoView);
43098                 return true;
43099             }
43100         }
43101         return false;
43102     },
43103
43104     /**
43105      * Select an item in the dropdown list by its numeric index in the list. 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 {Number} index The zero-based index of the list 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      */
43111     select : function(index, scrollIntoView){
43112         this.selectedIndex = index;
43113         this.view.select(index);
43114         if(scrollIntoView !== false){
43115             var el = this.view.getNode(index);
43116             if(el){
43117                 this.innerList.scrollChildIntoView(el, false);
43118             }
43119         }
43120     },
43121
43122     // private
43123     selectNext : function(){
43124         var ct = this.store.getCount();
43125         if(ct > 0){
43126             if(this.selectedIndex == -1){
43127                 this.select(0);
43128             }else if(this.selectedIndex < ct-1){
43129                 this.select(this.selectedIndex+1);
43130             }
43131         }
43132     },
43133
43134     // private
43135     selectPrev : function(){
43136         var ct = this.store.getCount();
43137         if(ct > 0){
43138             if(this.selectedIndex == -1){
43139                 this.select(0);
43140             }else if(this.selectedIndex != 0){
43141                 this.select(this.selectedIndex-1);
43142             }
43143         }
43144     },
43145
43146     // private
43147     onKeyUp : function(e){
43148         if(this.editable !== false && !e.isSpecialKey()){
43149             this.lastKey = e.getKey();
43150             this.dqTask.delay(this.queryDelay);
43151         }
43152     },
43153
43154     // private
43155     validateBlur : function(){
43156         return !this.list || !this.list.isVisible();   
43157     },
43158
43159     // private
43160     initQuery : function(){
43161         this.doQuery(this.getRawValue());
43162     },
43163
43164     // private
43165     doForce : function(){
43166         if(this.el.dom.value.length > 0){
43167             this.el.dom.value =
43168                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43169              
43170         }
43171     },
43172
43173     /**
43174      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43175      * query allowing the query action to be canceled if needed.
43176      * @param {String} query The SQL query to execute
43177      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43178      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43179      * saved in the current store (defaults to false)
43180      */
43181     doQuery : function(q, forceAll){
43182         if(q === undefined || q === null){
43183             q = '';
43184         }
43185         var qe = {
43186             query: q,
43187             forceAll: forceAll,
43188             combo: this,
43189             cancel:false
43190         };
43191         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43192             return false;
43193         }
43194         q = qe.query;
43195         forceAll = qe.forceAll;
43196         if(forceAll === true || (q.length >= this.minChars)){
43197             if(this.lastQuery != q || this.alwaysQuery){
43198                 this.lastQuery = q;
43199                 if(this.mode == 'local'){
43200                     this.selectedIndex = -1;
43201                     if(forceAll){
43202                         this.store.clearFilter();
43203                     }else{
43204                         this.store.filter(this.displayField, q);
43205                     }
43206                     this.onLoad();
43207                 }else{
43208                     this.store.baseParams[this.queryParam] = q;
43209                     this.store.load({
43210                         params: this.getParams(q)
43211                     });
43212                     this.expand();
43213                 }
43214             }else{
43215                 this.selectedIndex = -1;
43216                 this.onLoad();   
43217             }
43218         }
43219     },
43220
43221     // private
43222     getParams : function(q){
43223         var p = {};
43224         //p[this.queryParam] = q;
43225         if(this.pageSize){
43226             p.start = 0;
43227             p.limit = this.pageSize;
43228         }
43229         return p;
43230     },
43231
43232     /**
43233      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43234      */
43235     collapse : function(){
43236         if(!this.isExpanded()){
43237             return;
43238         }
43239         this.list.hide();
43240         Roo.get(document).un('mousedown', this.collapseIf, this);
43241         Roo.get(document).un('mousewheel', this.collapseIf, this);
43242         if (!this.editable) {
43243             Roo.get(document).un('keydown', this.listKeyPress, this);
43244         }
43245         this.fireEvent('collapse', this);
43246     },
43247
43248     // private
43249     collapseIf : function(e){
43250         if(!e.within(this.wrap) && !e.within(this.list)){
43251             this.collapse();
43252         }
43253     },
43254
43255     /**
43256      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43257      */
43258     expand : function(){
43259         if(this.isExpanded() || !this.hasFocus){
43260             return;
43261         }
43262         this.list.alignTo(this.el, this.listAlign);
43263         this.list.show();
43264         Roo.get(document).on('mousedown', this.collapseIf, this);
43265         Roo.get(document).on('mousewheel', this.collapseIf, this);
43266         if (!this.editable) {
43267             Roo.get(document).on('keydown', this.listKeyPress, this);
43268         }
43269         
43270         this.fireEvent('expand', this);
43271     },
43272
43273     // private
43274     // Implements the default empty TriggerField.onTriggerClick function
43275     onTriggerClick : function(){
43276         if(this.disabled){
43277             return;
43278         }
43279         if(this.isExpanded()){
43280             this.collapse();
43281             if (!this.blockFocus) {
43282                 this.el.focus();
43283             }
43284             
43285         }else {
43286             this.hasFocus = true;
43287             if(this.triggerAction == 'all') {
43288                 this.doQuery(this.allQuery, true);
43289             } else {
43290                 this.doQuery(this.getRawValue());
43291             }
43292             if (!this.blockFocus) {
43293                 this.el.focus();
43294             }
43295         }
43296     },
43297     listKeyPress : function(e)
43298     {
43299         //Roo.log('listkeypress');
43300         // scroll to first matching element based on key pres..
43301         if (e.isSpecialKey()) {
43302             return false;
43303         }
43304         var k = String.fromCharCode(e.getKey()).toUpperCase();
43305         //Roo.log(k);
43306         var match  = false;
43307         var csel = this.view.getSelectedNodes();
43308         var cselitem = false;
43309         if (csel.length) {
43310             var ix = this.view.indexOf(csel[0]);
43311             cselitem  = this.store.getAt(ix);
43312             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43313                 cselitem = false;
43314             }
43315             
43316         }
43317         
43318         this.store.each(function(v) { 
43319             if (cselitem) {
43320                 // start at existing selection.
43321                 if (cselitem.id == v.id) {
43322                     cselitem = false;
43323                 }
43324                 return;
43325             }
43326                 
43327             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43328                 match = this.store.indexOf(v);
43329                 return false;
43330             }
43331         }, this);
43332         
43333         if (match === false) {
43334             return true; // no more action?
43335         }
43336         // scroll to?
43337         this.view.select(match);
43338         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43339         sn.scrollIntoView(sn.dom.parentNode, false);
43340     } 
43341
43342     /** 
43343     * @cfg {Boolean} grow 
43344     * @hide 
43345     */
43346     /** 
43347     * @cfg {Number} growMin 
43348     * @hide 
43349     */
43350     /** 
43351     * @cfg {Number} growMax 
43352     * @hide 
43353     */
43354     /**
43355      * @hide
43356      * @method autoSize
43357      */
43358 });/*
43359  * Copyright(c) 2010-2012, Roo J Solutions Limited
43360  *
43361  * Licence LGPL
43362  *
43363  */
43364
43365 /**
43366  * @class Roo.form.ComboBoxArray
43367  * @extends Roo.form.TextField
43368  * A facebook style adder... for lists of email / people / countries  etc...
43369  * pick multiple items from a combo box, and shows each one.
43370  *
43371  *  Fred [x]  Brian [x]  [Pick another |v]
43372  *
43373  *
43374  *  For this to work: it needs various extra information
43375  *    - normal combo problay has
43376  *      name, hiddenName
43377  *    + displayField, valueField
43378  *
43379  *    For our purpose...
43380  *
43381  *
43382  *   If we change from 'extends' to wrapping...
43383  *   
43384  *  
43385  *
43386  
43387  
43388  * @constructor
43389  * Create a new ComboBoxArray.
43390  * @param {Object} config Configuration options
43391  */
43392  
43393
43394 Roo.form.ComboBoxArray = function(config)
43395 {
43396     this.addEvents({
43397         /**
43398          * @event beforeremove
43399          * Fires before remove the value from the list
43400              * @param {Roo.form.ComboBoxArray} _self This combo box array
43401              * @param {Roo.form.ComboBoxArray.Item} item removed item
43402              */
43403         'beforeremove' : true,
43404         /**
43405          * @event remove
43406          * Fires when remove the value from the list
43407              * @param {Roo.form.ComboBoxArray} _self This combo box array
43408              * @param {Roo.form.ComboBoxArray.Item} item removed item
43409              */
43410         'remove' : true
43411         
43412         
43413     });
43414     
43415     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43416     
43417     this.items = new Roo.util.MixedCollection(false);
43418     
43419     // construct the child combo...
43420     
43421     
43422     
43423     
43424    
43425     
43426 }
43427
43428  
43429 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43430
43431     /**
43432      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43433      */
43434     
43435     lastData : false,
43436     
43437     // behavies liek a hiddne field
43438     inputType:      'hidden',
43439     /**
43440      * @cfg {Number} width The width of the box that displays the selected element
43441      */ 
43442     width:          300,
43443
43444     
43445     
43446     /**
43447      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43448      */
43449     name : false,
43450     /**
43451      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43452      */
43453     hiddenName : false,
43454       /**
43455      * @cfg {String} seperator    The value seperator normally ',' 
43456      */
43457     seperator : ',',
43458     
43459     // private the array of items that are displayed..
43460     items  : false,
43461     // private - the hidden field el.
43462     hiddenEl : false,
43463     // private - the filed el..
43464     el : false,
43465     
43466     //validateValue : function() { return true; }, // all values are ok!
43467     //onAddClick: function() { },
43468     
43469     onRender : function(ct, position) 
43470     {
43471         
43472         // create the standard hidden element
43473         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43474         
43475         
43476         // give fake names to child combo;
43477         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43478         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43479         
43480         this.combo = Roo.factory(this.combo, Roo.form);
43481         this.combo.onRender(ct, position);
43482         if (typeof(this.combo.width) != 'undefined') {
43483             this.combo.onResize(this.combo.width,0);
43484         }
43485         
43486         this.combo.initEvents();
43487         
43488         // assigned so form know we need to do this..
43489         this.store          = this.combo.store;
43490         this.valueField     = this.combo.valueField;
43491         this.displayField   = this.combo.displayField ;
43492         
43493         
43494         this.combo.wrap.addClass('x-cbarray-grp');
43495         
43496         var cbwrap = this.combo.wrap.createChild(
43497             {tag: 'div', cls: 'x-cbarray-cb'},
43498             this.combo.el.dom
43499         );
43500         
43501              
43502         this.hiddenEl = this.combo.wrap.createChild({
43503             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43504         });
43505         this.el = this.combo.wrap.createChild({
43506             tag: 'input',  type:'hidden' , name: this.name, value : ''
43507         });
43508          //   this.el.dom.removeAttribute("name");
43509         
43510         
43511         this.outerWrap = this.combo.wrap;
43512         this.wrap = cbwrap;
43513         
43514         this.outerWrap.setWidth(this.width);
43515         this.outerWrap.dom.removeChild(this.el.dom);
43516         
43517         this.wrap.dom.appendChild(this.el.dom);
43518         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43519         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43520         
43521         this.combo.trigger.setStyle('position','relative');
43522         this.combo.trigger.setStyle('left', '0px');
43523         this.combo.trigger.setStyle('top', '2px');
43524         
43525         this.combo.el.setStyle('vertical-align', 'text-bottom');
43526         
43527         //this.trigger.setStyle('vertical-align', 'top');
43528         
43529         // this should use the code from combo really... on('add' ....)
43530         if (this.adder) {
43531             
43532         
43533             this.adder = this.outerWrap.createChild(
43534                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43535             var _t = this;
43536             this.adder.on('click', function(e) {
43537                 _t.fireEvent('adderclick', this, e);
43538             }, _t);
43539         }
43540         //var _t = this;
43541         //this.adder.on('click', this.onAddClick, _t);
43542         
43543         
43544         this.combo.on('select', function(cb, rec, ix) {
43545             this.addItem(rec.data);
43546             
43547             cb.setValue('');
43548             cb.el.dom.value = '';
43549             //cb.lastData = rec.data;
43550             // add to list
43551             
43552         }, this);
43553         
43554         
43555     },
43556     
43557     
43558     getName: function()
43559     {
43560         // returns hidden if it's set..
43561         if (!this.rendered) {return ''};
43562         return  this.hiddenName ? this.hiddenName : this.name;
43563         
43564     },
43565     
43566     
43567     onResize: function(w, h){
43568         
43569         return;
43570         // not sure if this is needed..
43571         //this.combo.onResize(w,h);
43572         
43573         if(typeof w != 'number'){
43574             // we do not handle it!?!?
43575             return;
43576         }
43577         var tw = this.combo.trigger.getWidth();
43578         tw += this.addicon ? this.addicon.getWidth() : 0;
43579         tw += this.editicon ? this.editicon.getWidth() : 0;
43580         var x = w - tw;
43581         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43582             
43583         this.combo.trigger.setStyle('left', '0px');
43584         
43585         if(this.list && this.listWidth === undefined){
43586             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43587             this.list.setWidth(lw);
43588             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43589         }
43590         
43591     
43592         
43593     },
43594     
43595     addItem: function(rec)
43596     {
43597         var valueField = this.combo.valueField;
43598         var displayField = this.combo.displayField;
43599         
43600         if (this.items.indexOfKey(rec[valueField]) > -1) {
43601             //console.log("GOT " + rec.data.id);
43602             return;
43603         }
43604         
43605         var x = new Roo.form.ComboBoxArray.Item({
43606             //id : rec[this.idField],
43607             data : rec,
43608             displayField : displayField ,
43609             tipField : displayField ,
43610             cb : this
43611         });
43612         // use the 
43613         this.items.add(rec[valueField],x);
43614         // add it before the element..
43615         this.updateHiddenEl();
43616         x.render(this.outerWrap, this.wrap.dom);
43617         // add the image handler..
43618     },
43619     
43620     updateHiddenEl : function()
43621     {
43622         this.validate();
43623         if (!this.hiddenEl) {
43624             return;
43625         }
43626         var ar = [];
43627         var idField = this.combo.valueField;
43628         
43629         this.items.each(function(f) {
43630             ar.push(f.data[idField]);
43631         });
43632         this.hiddenEl.dom.value = ar.join(this.seperator);
43633         this.validate();
43634     },
43635     
43636     reset : function()
43637     {
43638         this.items.clear();
43639         
43640         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43641            el.remove();
43642         });
43643         
43644         this.el.dom.value = '';
43645         if (this.hiddenEl) {
43646             this.hiddenEl.dom.value = '';
43647         }
43648         
43649     },
43650     getValue: function()
43651     {
43652         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43653     },
43654     setValue: function(v) // not a valid action - must use addItems..
43655     {
43656         
43657         this.reset();
43658          
43659         if (this.store.isLocal && (typeof(v) == 'string')) {
43660             // then we can use the store to find the values..
43661             // comma seperated at present.. this needs to allow JSON based encoding..
43662             this.hiddenEl.value  = v;
43663             var v_ar = [];
43664             Roo.each(v.split(this.seperator), function(k) {
43665                 Roo.log("CHECK " + this.valueField + ',' + k);
43666                 var li = this.store.query(this.valueField, k);
43667                 if (!li.length) {
43668                     return;
43669                 }
43670                 var add = {};
43671                 add[this.valueField] = k;
43672                 add[this.displayField] = li.item(0).data[this.displayField];
43673                 
43674                 this.addItem(add);
43675             }, this) 
43676              
43677         }
43678         if (typeof(v) == 'object' ) {
43679             // then let's assume it's an array of objects..
43680             Roo.each(v, function(l) {
43681                 var add = l;
43682                 if (typeof(l) == 'string') {
43683                     add = {};
43684                     add[this.valueField] = l;
43685                     add[this.displayField] = l
43686                 }
43687                 this.addItem(add);
43688             }, this);
43689              
43690         }
43691         
43692         
43693     },
43694     setFromData: function(v)
43695     {
43696         // this recieves an object, if setValues is called.
43697         this.reset();
43698         this.el.dom.value = v[this.displayField];
43699         this.hiddenEl.dom.value = v[this.valueField];
43700         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43701             return;
43702         }
43703         var kv = v[this.valueField];
43704         var dv = v[this.displayField];
43705         kv = typeof(kv) != 'string' ? '' : kv;
43706         dv = typeof(dv) != 'string' ? '' : dv;
43707         
43708         
43709         var keys = kv.split(this.seperator);
43710         var display = dv.split(this.seperator);
43711         for (var i = 0 ; i < keys.length; i++) {
43712             add = {};
43713             add[this.valueField] = keys[i];
43714             add[this.displayField] = display[i];
43715             this.addItem(add);
43716         }
43717       
43718         
43719     },
43720     
43721     /**
43722      * Validates the combox array value
43723      * @return {Boolean} True if the value is valid, else false
43724      */
43725     validate : function(){
43726         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43727             this.clearInvalid();
43728             return true;
43729         }
43730         return false;
43731     },
43732     
43733     validateValue : function(value){
43734         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43735         
43736     },
43737     
43738     /*@
43739      * overide
43740      * 
43741      */
43742     isDirty : function() {
43743         if(this.disabled) {
43744             return false;
43745         }
43746         
43747         try {
43748             var d = Roo.decode(String(this.originalValue));
43749         } catch (e) {
43750             return String(this.getValue()) !== String(this.originalValue);
43751         }
43752         
43753         var originalValue = [];
43754         
43755         for (var i = 0; i < d.length; i++){
43756             originalValue.push(d[i][this.valueField]);
43757         }
43758         
43759         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43760         
43761     }
43762     
43763 });
43764
43765
43766
43767 /**
43768  * @class Roo.form.ComboBoxArray.Item
43769  * @extends Roo.BoxComponent
43770  * A selected item in the list
43771  *  Fred [x]  Brian [x]  [Pick another |v]
43772  * 
43773  * @constructor
43774  * Create a new item.
43775  * @param {Object} config Configuration options
43776  */
43777  
43778 Roo.form.ComboBoxArray.Item = function(config) {
43779     config.id = Roo.id();
43780     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43781 }
43782
43783 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43784     data : {},
43785     cb: false,
43786     displayField : false,
43787     tipField : false,
43788     
43789     
43790     defaultAutoCreate : {
43791         tag: 'div',
43792         cls: 'x-cbarray-item',
43793         cn : [ 
43794             { tag: 'div' },
43795             {
43796                 tag: 'img',
43797                 width:16,
43798                 height : 16,
43799                 src : Roo.BLANK_IMAGE_URL ,
43800                 align: 'center'
43801             }
43802         ]
43803         
43804     },
43805     
43806  
43807     onRender : function(ct, position)
43808     {
43809         Roo.form.Field.superclass.onRender.call(this, ct, position);
43810         
43811         if(!this.el){
43812             var cfg = this.getAutoCreate();
43813             this.el = ct.createChild(cfg, position);
43814         }
43815         
43816         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43817         
43818         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43819             this.cb.renderer(this.data) :
43820             String.format('{0}',this.data[this.displayField]);
43821         
43822             
43823         this.el.child('div').dom.setAttribute('qtip',
43824                         String.format('{0}',this.data[this.tipField])
43825         );
43826         
43827         this.el.child('img').on('click', this.remove, this);
43828         
43829     },
43830    
43831     remove : function()
43832     {
43833         if(this.cb.disabled){
43834             return;
43835         }
43836         
43837         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43838             this.cb.items.remove(this);
43839             this.el.child('img').un('click', this.remove, this);
43840             this.el.remove();
43841             this.cb.updateHiddenEl();
43842
43843             this.cb.fireEvent('remove', this.cb, this);
43844         }
43845         
43846     }
43847 });/*
43848  * RooJS Library 1.1.1
43849  * Copyright(c) 2008-2011  Alan Knowles
43850  *
43851  * License - LGPL
43852  */
43853  
43854
43855 /**
43856  * @class Roo.form.ComboNested
43857  * @extends Roo.form.ComboBox
43858  * A combobox for that allows selection of nested items in a list,
43859  * eg.
43860  *
43861  *  Book
43862  *    -> red
43863  *    -> green
43864  *  Table
43865  *    -> square
43866  *      ->red
43867  *      ->green
43868  *    -> rectangle
43869  *      ->green
43870  *      
43871  * 
43872  * @constructor
43873  * Create a new ComboNested
43874  * @param {Object} config Configuration options
43875  */
43876 Roo.form.ComboNested = function(config){
43877     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43878     // should verify some data...
43879     // like
43880     // hiddenName = required..
43881     // displayField = required
43882     // valudField == required
43883     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43884     var _t = this;
43885     Roo.each(req, function(e) {
43886         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43887             throw "Roo.form.ComboNested : missing value for: " + e;
43888         }
43889     });
43890      
43891     
43892 };
43893
43894 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43895    
43896     /*
43897      * @config {Number} max Number of columns to show
43898      */
43899     
43900     maxColumns : 3,
43901    
43902     list : null, // the outermost div..
43903     innerLists : null, // the
43904     views : null,
43905     stores : null,
43906     // private
43907     loadingChildren : false,
43908     
43909     onRender : function(ct, position)
43910     {
43911         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43912         
43913         if(this.hiddenName){
43914             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43915                     'before', true);
43916             this.hiddenField.value =
43917                 this.hiddenValue !== undefined ? this.hiddenValue :
43918                 this.value !== undefined ? this.value : '';
43919
43920             // prevent input submission
43921             this.el.dom.removeAttribute('name');
43922              
43923              
43924         }
43925         
43926         if(Roo.isGecko){
43927             this.el.dom.setAttribute('autocomplete', 'off');
43928         }
43929
43930         var cls = 'x-combo-list';
43931
43932         this.list = new Roo.Layer({
43933             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43934         });
43935
43936         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43937         this.list.setWidth(lw);
43938         this.list.swallowEvent('mousewheel');
43939         this.assetHeight = 0;
43940
43941         if(this.title){
43942             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43943             this.assetHeight += this.header.getHeight();
43944         }
43945         this.innerLists = [];
43946         this.views = [];
43947         this.stores = [];
43948         for (var i =0 ; i < this.maxColumns; i++) {
43949             this.onRenderList( cls, i);
43950         }
43951         
43952         // always needs footer, as we are going to have an 'OK' button.
43953         this.footer = this.list.createChild({cls:cls+'-ft'});
43954         this.pageTb = new Roo.Toolbar(this.footer);  
43955         var _this = this;
43956         this.pageTb.add(  {
43957             
43958             text: 'Done',
43959             handler: function()
43960             {
43961                 _this.collapse();
43962             }
43963         });
43964         
43965         if ( this.allowBlank && !this.disableClear) {
43966             
43967             this.pageTb.add(new Roo.Toolbar.Fill(), {
43968                 cls: 'x-btn-icon x-btn-clear',
43969                 text: '&#160;',
43970                 handler: function()
43971                 {
43972                     _this.collapse();
43973                     _this.clearValue();
43974                     _this.onSelect(false, -1);
43975                 }
43976             });
43977         }
43978         if (this.footer) {
43979             this.assetHeight += this.footer.getHeight();
43980         }
43981         
43982     },
43983     onRenderList : function (  cls, i)
43984     {
43985         
43986         var lw = Math.floor(
43987                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43988         );
43989         
43990         this.list.setWidth(lw); // default to '1'
43991
43992         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43993         //il.on('mouseover', this.onViewOver, this, { list:  i });
43994         //il.on('mousemove', this.onViewMove, this, { list:  i });
43995         il.setWidth(lw);
43996         il.setStyle({ 'overflow-x' : 'hidden'});
43997
43998         if(!this.tpl){
43999             this.tpl = new Roo.Template({
44000                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44001                 isEmpty: function (value, allValues) {
44002                     //Roo.log(value);
44003                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44004                     return dl ? 'has-children' : 'no-children'
44005                 }
44006             });
44007         }
44008         
44009         var store  = this.store;
44010         if (i > 0) {
44011             store  = new Roo.data.SimpleStore({
44012                 //fields : this.store.reader.meta.fields,
44013                 reader : this.store.reader,
44014                 data : [ ]
44015             });
44016         }
44017         this.stores[i]  = store;
44018                   
44019         var view = this.views[i] = new Roo.View(
44020             il,
44021             this.tpl,
44022             {
44023                 singleSelect:true,
44024                 store: store,
44025                 selectedClass: this.selectedClass
44026             }
44027         );
44028         view.getEl().setWidth(lw);
44029         view.getEl().setStyle({
44030             position: i < 1 ? 'relative' : 'absolute',
44031             top: 0,
44032             left: (i * lw ) + 'px',
44033             display : i > 0 ? 'none' : 'block'
44034         });
44035         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44036         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44037         //view.on('click', this.onViewClick, this, { list : i });
44038
44039         store.on('beforeload', this.onBeforeLoad, this);
44040         store.on('load',  this.onLoad, this, { list  : i});
44041         store.on('loadexception', this.onLoadException, this);
44042
44043         // hide the other vies..
44044         
44045         
44046         
44047     },
44048       
44049     restrictHeight : function()
44050     {
44051         var mh = 0;
44052         Roo.each(this.innerLists, function(il,i) {
44053             var el = this.views[i].getEl();
44054             el.dom.style.height = '';
44055             var inner = el.dom;
44056             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44057             // only adjust heights on other ones..
44058             mh = Math.max(h, mh);
44059             if (i < 1) {
44060                 
44061                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44062                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44063                
44064             }
44065             
44066             
44067         }, this);
44068         
44069         this.list.beginUpdate();
44070         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44071         this.list.alignTo(this.el, this.listAlign);
44072         this.list.endUpdate();
44073         
44074     },
44075      
44076     
44077     // -- store handlers..
44078     // private
44079     onBeforeLoad : function()
44080     {
44081         if(!this.hasFocus){
44082             return;
44083         }
44084         this.innerLists[0].update(this.loadingText ?
44085                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44086         this.restrictHeight();
44087         this.selectedIndex = -1;
44088     },
44089     // private
44090     onLoad : function(a,b,c,d)
44091     {
44092         if (!this.loadingChildren) {
44093             // then we are loading the top level. - hide the children
44094             for (var i = 1;i < this.views.length; i++) {
44095                 this.views[i].getEl().setStyle({ display : 'none' });
44096             }
44097             var lw = Math.floor(
44098                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44099             );
44100         
44101              this.list.setWidth(lw); // default to '1'
44102
44103             
44104         }
44105         if(!this.hasFocus){
44106             return;
44107         }
44108         
44109         if(this.store.getCount() > 0) {
44110             this.expand();
44111             this.restrictHeight();   
44112         } else {
44113             this.onEmptyResults();
44114         }
44115         
44116         if (!this.loadingChildren) {
44117             this.selectActive();
44118         }
44119         /*
44120         this.stores[1].loadData([]);
44121         this.stores[2].loadData([]);
44122         this.views
44123         */    
44124     
44125         //this.el.focus();
44126     },
44127     
44128     
44129     // private
44130     onLoadException : function()
44131     {
44132         this.collapse();
44133         Roo.log(this.store.reader.jsonData);
44134         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44135             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44136         }
44137         
44138         
44139     },
44140     // no cleaning of leading spaces on blur here.
44141     cleanLeadingSpace : function(e) { },
44142     
44143
44144     onSelectChange : function (view, sels, opts )
44145     {
44146         var ix = view.getSelectedIndexes();
44147          
44148         if (opts.list > this.maxColumns - 2) {
44149             if (view.store.getCount()<  1) {
44150                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44151
44152             } else  {
44153                 if (ix.length) {
44154                     // used to clear ?? but if we are loading unselected 
44155                     this.setFromData(view.store.getAt(ix[0]).data);
44156                 }
44157                 
44158             }
44159             
44160             return;
44161         }
44162         
44163         if (!ix.length) {
44164             // this get's fired when trigger opens..
44165            // this.setFromData({});
44166             var str = this.stores[opts.list+1];
44167             str.data.clear(); // removeall wihtout the fire events..
44168             return;
44169         }
44170         
44171         var rec = view.store.getAt(ix[0]);
44172          
44173         this.setFromData(rec.data);
44174         this.fireEvent('select', this, rec, ix[0]);
44175         
44176         var lw = Math.floor(
44177              (
44178                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44179              ) / this.maxColumns
44180         );
44181         this.loadingChildren = true;
44182         this.stores[opts.list+1].loadDataFromChildren( rec );
44183         this.loadingChildren = false;
44184         var dl = this.stores[opts.list+1]. getTotalCount();
44185         
44186         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44187         
44188         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44189         for (var i = opts.list+2; i < this.views.length;i++) {
44190             this.views[i].getEl().setStyle({ display : 'none' });
44191         }
44192         
44193         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44194         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44195         
44196         if (this.isLoading) {
44197            // this.selectActive(opts.list);
44198         }
44199          
44200     },
44201     
44202     
44203     
44204     
44205     onDoubleClick : function()
44206     {
44207         this.collapse(); //??
44208     },
44209     
44210      
44211     
44212     
44213     
44214     // private
44215     recordToStack : function(store, prop, value, stack)
44216     {
44217         var cstore = new Roo.data.SimpleStore({
44218             //fields : this.store.reader.meta.fields, // we need array reader.. for
44219             reader : this.store.reader,
44220             data : [ ]
44221         });
44222         var _this = this;
44223         var record  = false;
44224         var srec = false;
44225         if(store.getCount() < 1){
44226             return false;
44227         }
44228         store.each(function(r){
44229             if(r.data[prop] == value){
44230                 record = r;
44231             srec = r;
44232                 return false;
44233             }
44234             if (r.data.cn && r.data.cn.length) {
44235                 cstore.loadDataFromChildren( r);
44236                 var cret = _this.recordToStack(cstore, prop, value, stack);
44237                 if (cret !== false) {
44238                     record = cret;
44239                     srec = r;
44240                     return false;
44241                 }
44242             }
44243              
44244             return true;
44245         });
44246         if (record == false) {
44247             return false
44248         }
44249         stack.unshift(srec);
44250         return record;
44251     },
44252     
44253     /*
44254      * find the stack of stores that match our value.
44255      *
44256      * 
44257      */
44258     
44259     selectActive : function ()
44260     {
44261         // if store is not loaded, then we will need to wait for that to happen first.
44262         var stack = [];
44263         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44264         for (var i = 0; i < stack.length; i++ ) {
44265             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44266         }
44267         
44268     }
44269         
44270          
44271     
44272     
44273     
44274     
44275 });/*
44276  * Based on:
44277  * Ext JS Library 1.1.1
44278  * Copyright(c) 2006-2007, Ext JS, LLC.
44279  *
44280  * Originally Released Under LGPL - original licence link has changed is not relivant.
44281  *
44282  * Fork - LGPL
44283  * <script type="text/javascript">
44284  */
44285 /**
44286  * @class Roo.form.Checkbox
44287  * @extends Roo.form.Field
44288  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44289  * @constructor
44290  * Creates a new Checkbox
44291  * @param {Object} config Configuration options
44292  */
44293 Roo.form.Checkbox = function(config){
44294     Roo.form.Checkbox.superclass.constructor.call(this, config);
44295     this.addEvents({
44296         /**
44297          * @event check
44298          * Fires when the checkbox is checked or unchecked.
44299              * @param {Roo.form.Checkbox} this This checkbox
44300              * @param {Boolean} checked The new checked value
44301              */
44302         check : true
44303     });
44304 };
44305
44306 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44307     /**
44308      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44309      */
44310     focusClass : undefined,
44311     /**
44312      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44313      */
44314     fieldClass: "x-form-field",
44315     /**
44316      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44317      */
44318     checked: false,
44319     /**
44320      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44321      * {tag: "input", type: "checkbox", autocomplete: "off"})
44322      */
44323     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44324     /**
44325      * @cfg {String} boxLabel The text that appears beside the checkbox
44326      */
44327     boxLabel : "",
44328     /**
44329      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44330      */  
44331     inputValue : '1',
44332     /**
44333      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44334      */
44335      valueOff: '0', // value when not checked..
44336
44337     actionMode : 'viewEl', 
44338     //
44339     // private
44340     itemCls : 'x-menu-check-item x-form-item',
44341     groupClass : 'x-menu-group-item',
44342     inputType : 'hidden',
44343     
44344     
44345     inSetChecked: false, // check that we are not calling self...
44346     
44347     inputElement: false, // real input element?
44348     basedOn: false, // ????
44349     
44350     isFormField: true, // not sure where this is needed!!!!
44351
44352     onResize : function(){
44353         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44354         if(!this.boxLabel){
44355             this.el.alignTo(this.wrap, 'c-c');
44356         }
44357     },
44358
44359     initEvents : function(){
44360         Roo.form.Checkbox.superclass.initEvents.call(this);
44361         this.el.on("click", this.onClick,  this);
44362         this.el.on("change", this.onClick,  this);
44363     },
44364
44365
44366     getResizeEl : function(){
44367         return this.wrap;
44368     },
44369
44370     getPositionEl : function(){
44371         return this.wrap;
44372     },
44373
44374     // private
44375     onRender : function(ct, position){
44376         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44377         /*
44378         if(this.inputValue !== undefined){
44379             this.el.dom.value = this.inputValue;
44380         }
44381         */
44382         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44383         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44384         var viewEl = this.wrap.createChild({ 
44385             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44386         this.viewEl = viewEl;   
44387         this.wrap.on('click', this.onClick,  this); 
44388         
44389         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44390         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44391         
44392         
44393         
44394         if(this.boxLabel){
44395             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44396         //    viewEl.on('click', this.onClick,  this); 
44397         }
44398         //if(this.checked){
44399             this.setChecked(this.checked);
44400         //}else{
44401             //this.checked = this.el.dom;
44402         //}
44403
44404     },
44405
44406     // private
44407     initValue : Roo.emptyFn,
44408
44409     /**
44410      * Returns the checked state of the checkbox.
44411      * @return {Boolean} True if checked, else false
44412      */
44413     getValue : function(){
44414         if(this.el){
44415             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44416         }
44417         return this.valueOff;
44418         
44419     },
44420
44421         // private
44422     onClick : function(){ 
44423         if (this.disabled) {
44424             return;
44425         }
44426         this.setChecked(!this.checked);
44427
44428         //if(this.el.dom.checked != this.checked){
44429         //    this.setValue(this.el.dom.checked);
44430        // }
44431     },
44432
44433     /**
44434      * Sets the checked state of the checkbox.
44435      * On is always based on a string comparison between inputValue and the param.
44436      * @param {Boolean/String} value - the value to set 
44437      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44438      */
44439     setValue : function(v,suppressEvent){
44440         
44441         
44442         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44443         //if(this.el && this.el.dom){
44444         //    this.el.dom.checked = this.checked;
44445         //    this.el.dom.defaultChecked = this.checked;
44446         //}
44447         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44448         //this.fireEvent("check", this, this.checked);
44449     },
44450     // private..
44451     setChecked : function(state,suppressEvent)
44452     {
44453         if (this.inSetChecked) {
44454             this.checked = state;
44455             return;
44456         }
44457         
44458     
44459         if(this.wrap){
44460             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44461         }
44462         this.checked = state;
44463         if(suppressEvent !== true){
44464             this.fireEvent('check', this, state);
44465         }
44466         this.inSetChecked = true;
44467         this.el.dom.value = state ? this.inputValue : this.valueOff;
44468         this.inSetChecked = false;
44469         
44470     },
44471     // handle setting of hidden value by some other method!!?!?
44472     setFromHidden: function()
44473     {
44474         if(!this.el){
44475             return;
44476         }
44477         //console.log("SET FROM HIDDEN");
44478         //alert('setFrom hidden');
44479         this.setValue(this.el.dom.value);
44480     },
44481     
44482     onDestroy : function()
44483     {
44484         if(this.viewEl){
44485             Roo.get(this.viewEl).remove();
44486         }
44487          
44488         Roo.form.Checkbox.superclass.onDestroy.call(this);
44489     },
44490     
44491     setBoxLabel : function(str)
44492     {
44493         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44494     }
44495
44496 });/*
44497  * Based on:
44498  * Ext JS Library 1.1.1
44499  * Copyright(c) 2006-2007, Ext JS, LLC.
44500  *
44501  * Originally Released Under LGPL - original licence link has changed is not relivant.
44502  *
44503  * Fork - LGPL
44504  * <script type="text/javascript">
44505  */
44506  
44507 /**
44508  * @class Roo.form.Radio
44509  * @extends Roo.form.Checkbox
44510  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44511  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44512  * @constructor
44513  * Creates a new Radio
44514  * @param {Object} config Configuration options
44515  */
44516 Roo.form.Radio = function(){
44517     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44518 };
44519 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44520     inputType: 'radio',
44521
44522     /**
44523      * If this radio is part of a group, it will return the selected value
44524      * @return {String}
44525      */
44526     getGroupValue : function(){
44527         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44528     },
44529     
44530     
44531     onRender : function(ct, position){
44532         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44533         
44534         if(this.inputValue !== undefined){
44535             this.el.dom.value = this.inputValue;
44536         }
44537          
44538         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44539         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44540         //var viewEl = this.wrap.createChild({ 
44541         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44542         //this.viewEl = viewEl;   
44543         //this.wrap.on('click', this.onClick,  this); 
44544         
44545         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44546         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44547         
44548         
44549         
44550         if(this.boxLabel){
44551             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44552         //    viewEl.on('click', this.onClick,  this); 
44553         }
44554          if(this.checked){
44555             this.el.dom.checked =   'checked' ;
44556         }
44557          
44558     } 
44559     
44560     
44561 });Roo.rtf = {}; // namespace
44562 Roo.rtf.Hex = function(hex)
44563 {
44564     this.hexstr = hex;
44565 };
44566 Roo.rtf.Paragraph = function(opts)
44567 {
44568     this.content = []; ///??? is that used?
44569 };Roo.rtf.Span = function(opts)
44570 {
44571     this.value = opts.value;
44572 };
44573
44574 Roo.rtf.Group = function(parent)
44575 {
44576     // we dont want to acutally store parent - it will make debug a nightmare..
44577     this.content = [];
44578     this.cn  = [];
44579      
44580        
44581     
44582 };
44583
44584 Roo.rtf.Group.prototype = {
44585     ignorable : false,
44586     content: false,
44587     cn: false,
44588     addContent : function(node) {
44589         // could set styles...
44590         this.content.push(node);
44591     },
44592     addChild : function(cn)
44593     {
44594         this.cn.push(cn);
44595     },
44596     // only for images really...
44597     toDataURL : function()
44598     {
44599         var mimetype = false;
44600         switch(true) {
44601             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44602                 mimetype = "image/png";
44603                 break;
44604              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44605                 mimetype = "image/jpeg";
44606                 break;
44607             default :
44608                 return 'about:blank'; // ?? error?
44609         }
44610         
44611         
44612         var hexstring = this.content[this.content.length-1].value;
44613         
44614         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44615             return String.fromCharCode(parseInt(a, 16));
44616         }).join(""));
44617     }
44618     
44619 };
44620 // this looks like it's normally the {rtf{ .... }}
44621 Roo.rtf.Document = function()
44622 {
44623     // we dont want to acutally store parent - it will make debug a nightmare..
44624     this.rtlch  = [];
44625     this.content = [];
44626     this.cn = [];
44627     
44628 };
44629 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44630     addChild : function(cn)
44631     {
44632         this.cn.push(cn);
44633         switch(cn.type) {
44634             case 'rtlch': // most content seems to be inside this??
44635             case 'listtext':
44636             case 'shpinst':
44637                 this.rtlch.push(cn);
44638                 return;
44639             default:
44640                 this[cn.type] = cn;
44641         }
44642         
44643     },
44644     
44645     getElementsByType : function(type)
44646     {
44647         var ret =  [];
44648         this._getElementsByType(type, ret, this.cn, 'rtf');
44649         return ret;
44650     },
44651     _getElementsByType : function (type, ret, search_array, path)
44652     {
44653         search_array.forEach(function(n,i) {
44654             if (n.type == type) {
44655                 n.path = path + '/' + n.type + ':' + i;
44656                 ret.push(n);
44657             }
44658             if (n.cn.length > 0) {
44659                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44660             }
44661         },this);
44662     }
44663     
44664 });
44665  
44666 Roo.rtf.Ctrl = function(opts)
44667 {
44668     this.value = opts.value;
44669     this.param = opts.param;
44670 };
44671 /**
44672  *
44673  *
44674  * based on this https://github.com/iarna/rtf-parser
44675  * it's really only designed to extract pict from pasted RTF 
44676  *
44677  * usage:
44678  *
44679  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44680  *  
44681  *
44682  */
44683
44684  
44685
44686
44687
44688 Roo.rtf.Parser = function(text) {
44689     //super({objectMode: true})
44690     this.text = '';
44691     this.parserState = this.parseText;
44692     
44693     // these are for interpeter...
44694     this.doc = {};
44695     ///this.parserState = this.parseTop
44696     this.groupStack = [];
44697     this.hexStore = [];
44698     this.doc = false;
44699     
44700     this.groups = []; // where we put the return.
44701     
44702     for (var ii = 0; ii < text.length; ++ii) {
44703         ++this.cpos;
44704         
44705         if (text[ii] === '\n') {
44706             ++this.row;
44707             this.col = 1;
44708         } else {
44709             ++this.col;
44710         }
44711         this.parserState(text[ii]);
44712     }
44713     
44714     
44715     
44716 };
44717 Roo.rtf.Parser.prototype = {
44718     text : '', // string being parsed..
44719     controlWord : '',
44720     controlWordParam :  '',
44721     hexChar : '',
44722     doc : false,
44723     group: false,
44724     groupStack : false,
44725     hexStore : false,
44726     
44727     
44728     cpos : 0, 
44729     row : 1, // reportin?
44730     col : 1, //
44731
44732      
44733     push : function (el)
44734     {
44735         var m = 'cmd'+ el.type;
44736         if (typeof(this[m]) == 'undefined') {
44737             Roo.log('invalid cmd:' + el.type);
44738             return;
44739         }
44740         this[m](el);
44741         //Roo.log(el);
44742     },
44743     flushHexStore : function()
44744     {
44745         if (this.hexStore.length < 1) {
44746             return;
44747         }
44748         var hexstr = this.hexStore.map(
44749             function(cmd) {
44750                 return cmd.value;
44751         }).join('');
44752         
44753         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44754               
44755             
44756         this.hexStore.splice(0)
44757         
44758     },
44759     
44760     cmdgroupstart : function()
44761     {
44762         this.flushHexStore();
44763         if (this.group) {
44764             this.groupStack.push(this.group);
44765         }
44766          // parent..
44767         if (this.doc === false) {
44768             this.group = this.doc = new Roo.rtf.Document();
44769             return;
44770             
44771         }
44772         this.group = new Roo.rtf.Group(this.group);
44773     },
44774     cmdignorable : function()
44775     {
44776         this.flushHexStore();
44777         this.group.ignorable = true;
44778     },
44779     cmdendparagraph : function()
44780     {
44781         this.flushHexStore();
44782         this.group.addContent(new Roo.rtf.Paragraph());
44783     },
44784     cmdgroupend : function ()
44785     {
44786         this.flushHexStore();
44787         var endingGroup = this.group;
44788         
44789         
44790         this.group = this.groupStack.pop();
44791         if (this.group) {
44792             this.group.addChild(endingGroup);
44793         }
44794         
44795         
44796         
44797         var doc = this.group || this.doc;
44798         //if (endingGroup instanceof FontTable) {
44799         //  doc.fonts = endingGroup.table
44800         //} else if (endingGroup instanceof ColorTable) {
44801         //  doc.colors = endingGroup.table
44802         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44803         if (endingGroup.ignorable === false) {
44804             //code
44805             this.groups.push(endingGroup);
44806            // Roo.log( endingGroup );
44807         }
44808             //Roo.each(endingGroup.content, function(item)) {
44809             //    doc.addContent(item);
44810             //}
44811             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44812         //}
44813     },
44814     cmdtext : function (cmd)
44815     {
44816         this.flushHexStore();
44817         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44818             //this.group = this.doc
44819         }
44820         this.group.addContent(new Roo.rtf.Span(cmd));
44821     },
44822     cmdcontrolword : function (cmd)
44823     {
44824         this.flushHexStore();
44825         if (!this.group.type) {
44826             this.group.type = cmd.value;
44827             return;
44828         }
44829         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44830         // we actually don't care about ctrl words...
44831         return ;
44832         /*
44833         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44834         if (this[method]) {
44835             this[method](cmd.param)
44836         } else {
44837             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44838         }
44839         */
44840     },
44841     cmdhexchar : function(cmd) {
44842         this.hexStore.push(cmd);
44843     },
44844     cmderror : function(cmd) {
44845         throw new Exception (cmd.value);
44846     },
44847     
44848     /*
44849       _flush (done) {
44850         if (this.text !== '\u0000') this.emitText()
44851         done()
44852       }
44853       */
44854       
44855       
44856     parseText : function(c)
44857     {
44858         if (c === '\\') {
44859             this.parserState = this.parseEscapes;
44860         } else if (c === '{') {
44861             this.emitStartGroup();
44862         } else if (c === '}') {
44863             this.emitEndGroup();
44864         } else if (c === '\x0A' || c === '\x0D') {
44865             // cr/lf are noise chars
44866         } else {
44867             this.text += c;
44868         }
44869     },
44870     
44871     parseEscapes: function (c)
44872     {
44873         if (c === '\\' || c === '{' || c === '}') {
44874             this.text += c;
44875             this.parserState = this.parseText;
44876         } else {
44877             this.parserState = this.parseControlSymbol;
44878             this.parseControlSymbol(c);
44879         }
44880     },
44881     parseControlSymbol: function(c)
44882     {
44883         if (c === '~') {
44884             this.text += '\u00a0'; // nbsp
44885             this.parserState = this.parseText
44886         } else if (c === '-') {
44887              this.text += '\u00ad'; // soft hyphen
44888         } else if (c === '_') {
44889             this.text += '\u2011'; // non-breaking hyphen
44890         } else if (c === '*') {
44891             this.emitIgnorable();
44892             this.parserState = this.parseText;
44893         } else if (c === "'") {
44894             this.parserState = this.parseHexChar;
44895         } else if (c === '|') { // formula cacter
44896             this.emitFormula();
44897             this.parserState = this.parseText;
44898         } else if (c === ':') { // subentry in an index entry
44899             this.emitIndexSubEntry();
44900             this.parserState = this.parseText;
44901         } else if (c === '\x0a') {
44902             this.emitEndParagraph();
44903             this.parserState = this.parseText;
44904         } else if (c === '\x0d') {
44905             this.emitEndParagraph();
44906             this.parserState = this.parseText;
44907         } else {
44908             this.parserState = this.parseControlWord;
44909             this.parseControlWord(c);
44910         }
44911     },
44912     parseHexChar: function (c)
44913     {
44914         if (/^[A-Fa-f0-9]$/.test(c)) {
44915             this.hexChar += c;
44916             if (this.hexChar.length >= 2) {
44917               this.emitHexChar();
44918               this.parserState = this.parseText;
44919             }
44920             return;
44921         }
44922         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44923         this.parserState = this.parseText;
44924         
44925     },
44926     parseControlWord : function(c)
44927     {
44928         if (c === ' ') {
44929             this.emitControlWord();
44930             this.parserState = this.parseText;
44931         } else if (/^[-\d]$/.test(c)) {
44932             this.parserState = this.parseControlWordParam;
44933             this.controlWordParam += c;
44934         } else if (/^[A-Za-z]$/.test(c)) {
44935           this.controlWord += c;
44936         } else {
44937           this.emitControlWord();
44938           this.parserState = this.parseText;
44939           this.parseText(c);
44940         }
44941     },
44942     parseControlWordParam : function (c) {
44943         if (/^\d$/.test(c)) {
44944           this.controlWordParam += c;
44945         } else if (c === ' ') {
44946           this.emitControlWord();
44947           this.parserState = this.parseText;
44948         } else {
44949           this.emitControlWord();
44950           this.parserState = this.parseText;
44951           this.parseText(c);
44952         }
44953     },
44954     
44955     
44956     
44957     
44958     emitText : function () {
44959         if (this.text === '') {
44960             return;
44961         }
44962         this.push({
44963             type: 'text',
44964             value: this.text,
44965             pos: this.cpos,
44966             row: this.row,
44967             col: this.col
44968         });
44969         this.text = ''
44970     },
44971     emitControlWord : function ()
44972     {
44973         this.emitText();
44974         if (this.controlWord === '') {
44975             this.emitError('empty control word');
44976         } else {
44977             this.push({
44978                   type: 'controlword',
44979                   value: this.controlWord,
44980                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
44981                   pos: this.cpos,
44982                   row: this.row,
44983                   col: this.col
44984             });
44985         }
44986         this.controlWord = '';
44987         this.controlWordParam = '';
44988     },
44989     emitStartGroup : function ()
44990     {
44991         this.emitText();
44992         this.push({
44993             type: 'groupstart',
44994             pos: this.cpos,
44995             row: this.row,
44996             col: this.col
44997         });
44998     },
44999     emitEndGroup : function ()
45000     {
45001         this.emitText();
45002         this.push({
45003             type: 'groupend',
45004             pos: this.cpos,
45005             row: this.row,
45006             col: this.col
45007         });
45008     },
45009     emitIgnorable : function ()
45010     {
45011         this.emitText();
45012         this.push({
45013             type: 'ignorable',
45014             pos: this.cpos,
45015             row: this.row,
45016             col: this.col
45017         });
45018     },
45019     emitHexChar : function ()
45020     {
45021         this.emitText();
45022         this.push({
45023             type: 'hexchar',
45024             value: this.hexChar,
45025             pos: this.cpos,
45026             row: this.row,
45027             col: this.col
45028         });
45029         this.hexChar = ''
45030     },
45031     emitError : function (message)
45032     {
45033       this.emitText();
45034       this.push({
45035             type: 'error',
45036             value: message,
45037             row: this.row,
45038             col: this.col,
45039             char: this.cpos //,
45040             //stack: new Error().stack
45041         });
45042     },
45043     emitEndParagraph : function () {
45044         this.emitText();
45045         this.push({
45046             type: 'endparagraph',
45047             pos: this.cpos,
45048             row: this.row,
45049             col: this.col
45050         });
45051     }
45052      
45053 } ;
45054 Roo.htmleditor = {};
45055  
45056 /**
45057  * @class Roo.htmleditor.Filter
45058  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45059  * @cfg {DomElement} node The node to iterate and filter
45060  * @cfg {boolean|String|Array} tag Tags to replace 
45061  * @constructor
45062  * Create a new Filter.
45063  * @param {Object} config Configuration options
45064  */
45065
45066
45067
45068 Roo.htmleditor.Filter = function(cfg) {
45069     Roo.apply(this.cfg);
45070     // this does not actually call walk as it's really just a abstract class
45071 }
45072
45073
45074 Roo.htmleditor.Filter.prototype = {
45075     
45076     node: false,
45077     
45078     tag: false,
45079
45080     // overrride to do replace comments.
45081     replaceComment : false,
45082     
45083     // overrride to do replace or do stuff with tags..
45084     replaceTag : false,
45085     
45086     walk : function(dom)
45087     {
45088         Roo.each( Array.from(dom.childNodes), function( e ) {
45089             switch(true) {
45090                 
45091                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45092                     this.replaceComment(e);
45093                     return;
45094                 
45095                 case e.nodeType != 1: //not a node.
45096                     return;
45097                 
45098                 case this.tag === true: // everything
45099                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45100                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45101                     if (this.replaceTag && false === this.replaceTag(e)) {
45102                         return;
45103                     }
45104                     if (e.hasChildNodes()) {
45105                         this.walk(e);
45106                     }
45107                     return;
45108                 
45109                 default:    // tags .. that do not match.
45110                     if (e.hasChildNodes()) {
45111                         this.walk(e);
45112                     }
45113             }
45114             
45115         }, this);
45116         
45117     }
45118 }; 
45119
45120 /**
45121  * @class Roo.htmleditor.FilterAttributes
45122  * clean attributes and  styles including http:// etc.. in attribute
45123  * @constructor
45124 * Run a new Attribute Filter
45125 * @param {Object} config Configuration options
45126  */
45127 Roo.htmleditor.FilterAttributes = function(cfg)
45128 {
45129     Roo.apply(this, cfg);
45130     this.attrib_black = this.attrib_black || [];
45131     this.attrib_white = this.attrib_white || [];
45132
45133     this.attrib_clean = this.attrib_clean || [];
45134     this.style_white = this.style_white || [];
45135     this.style_black = this.style_black || [];
45136     this.walk(cfg.node);
45137 }
45138
45139 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45140 {
45141     tag: true, // all tags
45142     
45143     attrib_black : false, // array
45144     attrib_clean : false,
45145     attrib_white : false,
45146
45147     style_white : false,
45148     style_black : false,
45149      
45150      
45151     replaceTag : function(node)
45152     {
45153         if (!node.attributes || !node.attributes.length) {
45154             return true;
45155         }
45156         
45157         for (var i = node.attributes.length-1; i > -1 ; i--) {
45158             var a = node.attributes[i];
45159             //console.log(a);
45160             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45161                 node.removeAttribute(a.name);
45162                 continue;
45163             }
45164             
45165             
45166             
45167             if (a.name.toLowerCase().substr(0,2)=='on')  {
45168                 node.removeAttribute(a.name);
45169                 continue;
45170             }
45171             
45172             
45173             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45174                 node.removeAttribute(a.name);
45175                 continue;
45176             }
45177             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45178                 this.cleanAttr(node,a.name,a.value); // fixme..
45179                 continue;
45180             }
45181             if (a.name == 'style') {
45182                 this.cleanStyle(node,a.name,a.value);
45183                 continue;
45184             }
45185             /// clean up MS crap..
45186             // tecnically this should be a list of valid class'es..
45187             
45188             
45189             if (a.name == 'class') {
45190                 if (a.value.match(/^Mso/)) {
45191                     node.removeAttribute('class');
45192                 }
45193                 
45194                 if (a.value.match(/^body$/)) {
45195                     node.removeAttribute('class');
45196                 }
45197                 continue;
45198             }
45199             
45200             
45201             // style cleanup!?
45202             // class cleanup?
45203             
45204         }
45205         return true; // clean children
45206     },
45207         
45208     cleanAttr: function(node, n,v)
45209     {
45210         
45211         if (v.match(/^\./) || v.match(/^\//)) {
45212             return;
45213         }
45214         if (v.match(/^(http|https):\/\//)
45215             || v.match(/^mailto:/) 
45216             || v.match(/^ftp:/)
45217             || v.match(/^data:/)
45218             ) {
45219             return;
45220         }
45221         if (v.match(/^#/)) {
45222             return;
45223         }
45224         if (v.match(/^\{/)) { // allow template editing.
45225             return;
45226         }
45227 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45228         node.removeAttribute(n);
45229         
45230     },
45231     cleanStyle : function(node,  n,v)
45232     {
45233         if (v.match(/expression/)) { //XSS?? should we even bother..
45234             node.removeAttribute(n);
45235             return;
45236         }
45237         
45238         var parts = v.split(/;/);
45239         var clean = [];
45240         
45241         Roo.each(parts, function(p) {
45242             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45243             if (!p.length) {
45244                 return true;
45245             }
45246             var l = p.split(':').shift().replace(/\s+/g,'');
45247             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45248             
45249             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45250                 return true;
45251             }
45252             //Roo.log()
45253             // only allow 'c whitelisted system attributes'
45254             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45255                 return true;
45256             }
45257             
45258             
45259             clean.push(p);
45260             return true;
45261         },this);
45262         if (clean.length) { 
45263             node.setAttribute(n, clean.join(';'));
45264         } else {
45265             node.removeAttribute(n);
45266         }
45267         
45268     }
45269         
45270         
45271         
45272     
45273 });/**
45274  * @class Roo.htmleditor.FilterBlack
45275  * remove blacklisted elements.
45276  * @constructor
45277  * Run a new Blacklisted Filter
45278  * @param {Object} config Configuration options
45279  */
45280
45281 Roo.htmleditor.FilterBlack = function(cfg)
45282 {
45283     Roo.apply(this, cfg);
45284     this.walk(cfg.node);
45285 }
45286
45287 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45288 {
45289     tag : true, // all elements.
45290    
45291     replace : function(n)
45292     {
45293         n.parentNode.removeChild(n);
45294     }
45295 });
45296 /**
45297  * @class Roo.htmleditor.FilterComment
45298  * remove comments.
45299  * @constructor
45300 * Run a new Comments Filter
45301 * @param {Object} config Configuration options
45302  */
45303 Roo.htmleditor.FilterComment = function(cfg)
45304 {
45305     this.walk(cfg.node);
45306 }
45307
45308 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45309 {
45310   
45311     replaceComment : function(n)
45312     {
45313         n.parentNode.removeChild(n);
45314     }
45315 });/**
45316  * @class Roo.htmleditor.FilterKeepChildren
45317  * remove tags but keep children
45318  * @constructor
45319  * Run a new Keep Children Filter
45320  * @param {Object} config Configuration options
45321  */
45322
45323 Roo.htmleditor.FilterKeepChildren = function(cfg)
45324 {
45325     Roo.apply(this, cfg);
45326     if (this.tag === false) {
45327         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45328     }
45329     this.walk(cfg.node);
45330 }
45331
45332 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45333 {
45334     
45335   
45336     replaceTag : function(node)
45337     {
45338         // walk children...
45339         //Roo.log(node);
45340         var ar = Array.from(node.childNodes);
45341         //remove first..
45342         for (var i = 0; i < ar.length; i++) {
45343             if (ar[i].nodeType == 1) {
45344                 if (
45345                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45346                     || // array and it matches
45347                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45348                 ) {
45349                     this.replaceTag(ar[i]); // child is blacklisted as well...
45350                     continue;
45351                 }
45352             }
45353         }  
45354         ar = Array.from(node.childNodes);
45355         for (var i = 0; i < ar.length; i++) {
45356          
45357             node.removeChild(ar[i]);
45358             // what if we need to walk these???
45359             node.parentNode.insertBefore(ar[i], node);
45360             if (this.tag !== false) {
45361                 this.walk(ar[i]);
45362                 
45363             }
45364         }
45365         node.parentNode.removeChild(node);
45366         return false; // don't walk children
45367         
45368         
45369     }
45370 });/**
45371  * @class Roo.htmleditor.FilterParagraph
45372  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45373  * like on 'push' to remove the <p> tags and replace them with line breaks.
45374  * @constructor
45375  * Run a new Paragraph Filter
45376  * @param {Object} config Configuration options
45377  */
45378
45379 Roo.htmleditor.FilterParagraph = function(cfg)
45380 {
45381     // no need to apply config.
45382     this.walk(cfg.node);
45383 }
45384
45385 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45386 {
45387     
45388      
45389     tag : 'P',
45390     
45391      
45392     replaceTag : function(node)
45393     {
45394         
45395         if (node.childNodes.length == 1 &&
45396             node.childNodes[0].nodeType == 3 &&
45397             node.childNodes[0].textContent.trim().length < 1
45398             ) {
45399             // remove and replace with '<BR>';
45400             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45401             return false; // no need to walk..
45402         }
45403         var ar = Array.from(node.childNodes);
45404         for (var i = 0; i < ar.length; i++) {
45405             node.removeChild(ar[i]);
45406             // what if we need to walk these???
45407             node.parentNode.insertBefore(ar[i], node);
45408         }
45409         // now what about this?
45410         // <p> &nbsp; </p>
45411         
45412         // double BR.
45413         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45414         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45415         node.parentNode.removeChild(node);
45416         
45417         return false;
45418
45419     }
45420     
45421 });/**
45422  * @class Roo.htmleditor.FilterSpan
45423  * filter span's with no attributes out..
45424  * @constructor
45425  * Run a new Span Filter
45426  * @param {Object} config Configuration options
45427  */
45428
45429 Roo.htmleditor.FilterSpan = function(cfg)
45430 {
45431     // no need to apply config.
45432     this.walk(cfg.node);
45433 }
45434
45435 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45436 {
45437      
45438     tag : 'SPAN',
45439      
45440  
45441     replaceTag : function(node)
45442     {
45443         if (node.attributes && node.attributes.length > 0) {
45444             return true; // walk if there are any.
45445         }
45446         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45447         return false;
45448      
45449     }
45450     
45451 });/**
45452  * @class Roo.htmleditor.FilterTableWidth
45453   try and remove table width data - as that frequently messes up other stuff.
45454  * 
45455  *      was cleanTableWidths.
45456  *
45457  * Quite often pasting from word etc.. results in tables with column and widths.
45458  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45459  *
45460  * @constructor
45461  * Run a new Table Filter
45462  * @param {Object} config Configuration options
45463  */
45464
45465 Roo.htmleditor.FilterTableWidth = function(cfg)
45466 {
45467     // no need to apply config.
45468     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45469     this.walk(cfg.node);
45470 }
45471
45472 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45473 {
45474      
45475      
45476     
45477     replaceTag: function(node) {
45478         
45479         
45480       
45481         if (node.hasAttribute('width')) {
45482             node.removeAttribute('width');
45483         }
45484         
45485          
45486         if (node.hasAttribute("style")) {
45487             // pretty basic...
45488             
45489             var styles = node.getAttribute("style").split(";");
45490             var nstyle = [];
45491             Roo.each(styles, function(s) {
45492                 if (!s.match(/:/)) {
45493                     return;
45494                 }
45495                 var kv = s.split(":");
45496                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45497                     return;
45498                 }
45499                 // what ever is left... we allow.
45500                 nstyle.push(s);
45501             });
45502             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45503             if (!nstyle.length) {
45504                 node.removeAttribute('style');
45505             }
45506         }
45507         
45508         return true; // continue doing children..
45509     }
45510 });/**
45511  * @class Roo.htmleditor.FilterWord
45512  * try and clean up all the mess that Word generates.
45513  * 
45514  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45515  
45516  * @constructor
45517  * Run a new Span Filter
45518  * @param {Object} config Configuration options
45519  */
45520
45521 Roo.htmleditor.FilterWord = function(cfg)
45522 {
45523     // no need to apply config.
45524     this.walk(cfg.node);
45525 }
45526
45527 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45528 {
45529     tag: true,
45530      
45531     
45532     /**
45533      * Clean up MS wordisms...
45534      */
45535     replaceTag : function(node)
45536     {
45537          
45538         // no idea what this does - span with text, replaceds with just text.
45539         if(
45540                 node.nodeName == 'SPAN' &&
45541                 !node.hasAttributes() &&
45542                 node.childNodes.length == 1 &&
45543                 node.firstChild.nodeName == "#text"  
45544         ) {
45545             var textNode = node.firstChild;
45546             node.removeChild(textNode);
45547             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45548                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45549             }
45550             node.parentNode.insertBefore(textNode, node);
45551             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45552                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45553             }
45554             
45555             node.parentNode.removeChild(node);
45556             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45557         }
45558         
45559    
45560         
45561         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45562             node.parentNode.removeChild(node);
45563             return false; // dont do chidlren
45564         }
45565         //Roo.log(node.tagName);
45566         // remove - but keep children..
45567         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45568             //Roo.log('-- removed');
45569             while (node.childNodes.length) {
45570                 var cn = node.childNodes[0];
45571                 node.removeChild(cn);
45572                 node.parentNode.insertBefore(cn, node);
45573                 // move node to parent - and clean it..
45574                 this.replaceTag(cn);
45575             }
45576             node.parentNode.removeChild(node);
45577             /// no need to iterate chidlren = it's got none..
45578             //this.iterateChildren(node, this.cleanWord);
45579             return false; // no need to iterate children.
45580         }
45581         // clean styles
45582         if (node.className.length) {
45583             
45584             var cn = node.className.split(/\W+/);
45585             var cna = [];
45586             Roo.each(cn, function(cls) {
45587                 if (cls.match(/Mso[a-zA-Z]+/)) {
45588                     return;
45589                 }
45590                 cna.push(cls);
45591             });
45592             node.className = cna.length ? cna.join(' ') : '';
45593             if (!cna.length) {
45594                 node.removeAttribute("class");
45595             }
45596         }
45597         
45598         if (node.hasAttribute("lang")) {
45599             node.removeAttribute("lang");
45600         }
45601         
45602         if (node.hasAttribute("style")) {
45603             
45604             var styles = node.getAttribute("style").split(";");
45605             var nstyle = [];
45606             Roo.each(styles, function(s) {
45607                 if (!s.match(/:/)) {
45608                     return;
45609                 }
45610                 var kv = s.split(":");
45611                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45612                     return;
45613                 }
45614                 // what ever is left... we allow.
45615                 nstyle.push(s);
45616             });
45617             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45618             if (!nstyle.length) {
45619                 node.removeAttribute('style');
45620             }
45621         }
45622         return true; // do children
45623         
45624         
45625         
45626     }
45627 });
45628 /**
45629  * @class Roo.htmleditor.FilterStyleToTag
45630  * part of the word stuff... - certain 'styles' should be converted to tags.
45631  * eg.
45632  *   font-weight: bold -> bold
45633  *   ?? super / subscrit etc..
45634  * 
45635  * @constructor
45636 * Run a new style to tag filter.
45637 * @param {Object} config Configuration options
45638  */
45639 Roo.htmleditor.FilterStyleToTag = function(cfg)
45640 {
45641     
45642     this.tags = {
45643         B  : [ 'fontWeight' , 'bold'],
45644         I :  [ 'fontStyle' , 'italic'],
45645         //pre :  [ 'font-style' , 'italic'],
45646         // h1.. h6 ?? font-size?
45647         SUP : [ 'verticalAlign' , 'super' ],
45648         SUB : [ 'verticalAlign' , 'sub' ]
45649         
45650         
45651     };
45652     
45653     Roo.apply(this, cfg);
45654      
45655     
45656     this.walk(cfg.node);
45657     
45658     
45659     
45660 }
45661
45662
45663 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45664 {
45665     tag: true, // all tags
45666     
45667     tags : false,
45668     
45669     
45670     replaceTag : function(node)
45671     {
45672         
45673         
45674         if (node.getAttribute("style") === null) {
45675             return true;
45676         }
45677         var inject = [];
45678         for (var k in this.tags) {
45679             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45680                 inject.push(k);
45681                 node.style.removeProperty(this.tags[k][0]);
45682             }
45683         }
45684         if (!inject.length) {
45685             return true; 
45686         }
45687         var cn = Array.from(node.childNodes);
45688         var nn = node;
45689         Roo.each(inject, function(t) {
45690             var nc = node.ownerDocument.createelement(t);
45691             nn.appendChild(nc);
45692             nn = nc;
45693         });
45694         for(var i = 0;i < cn.length;cn++) {
45695             node.removeChild(cn[i]);
45696             nn.appendChild(cn[i]);
45697         }
45698         return true /// iterate thru
45699     }
45700     
45701 })/**
45702  * @class Roo.htmleditor.FilterLongBr
45703  * BR/BR/BR - keep a maximum of 2...
45704  * @constructor
45705  * Run a new Long BR Filter
45706  * @param {Object} config Configuration options
45707  */
45708
45709 Roo.htmleditor.FilterLongBr = function(cfg)
45710 {
45711     // no need to apply config.
45712     this.walk(cfg.node);
45713 }
45714
45715 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45716 {
45717     
45718      
45719     tag : 'BR',
45720     
45721      
45722     replaceTag : function(node)
45723     {
45724         
45725         var ps = node.nextSibling;
45726         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45727             ps = ps.nextSibling;
45728         }
45729         
45730         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45731             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45732             return false;
45733         }
45734         
45735         if (!ps || ps.nodeType != 1) {
45736             return false;
45737         }
45738         
45739         if (!ps || ps.tagName != 'BR') {
45740            
45741             return false;
45742         }
45743         
45744         
45745         
45746         
45747         
45748         if (!node.previousSibling) {
45749             return false;
45750         }
45751         var ps = node.previousSibling;
45752         
45753         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45754             ps = ps.previousSibling;
45755         }
45756         if (!ps || ps.nodeType != 1) {
45757             return false;
45758         }
45759         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45760         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45761             return false;
45762         }
45763         
45764         node.parentNode.removeChild(node); // remove me...
45765         
45766         return false; // no need to do children
45767
45768     }
45769     
45770 });
45771 /**
45772  * @class Roo.htmleditor.Tidy
45773  * Tidy HTML 
45774  * @cfg {Roo.HtmlEditorCore} core the editor.
45775  * @constructor
45776  * Create a new Filter.
45777  * @param {Object} config Configuration options
45778  */
45779
45780
45781 Roo.htmleditor.Tidy = function(cfg) {
45782     Roo.apply(this, cfg);
45783     
45784     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45785      
45786 }
45787
45788 Roo.htmleditor.Tidy.toString = function(node)
45789 {
45790     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45791 }
45792
45793 Roo.htmleditor.Tidy.prototype = {
45794     
45795     
45796     wrap : function(s) {
45797         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45798     },
45799
45800     
45801     tidy : function(node, indent) {
45802      
45803         if  (node.nodeType == 3) {
45804             // text.
45805             
45806             
45807             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45808                 
45809             
45810         }
45811         
45812         if  (node.nodeType != 1) {
45813             return '';
45814         }
45815         
45816         
45817         
45818         if (node.tagName == 'BODY') {
45819             
45820             return this.cn(node, '');
45821         }
45822              
45823              // Prints the node tagName, such as <A>, <IMG>, etc
45824         var ret = "<" + node.tagName +  this.attr(node) ;
45825         
45826         // elements with no children..
45827         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45828                 return ret + '/>';
45829         }
45830         ret += '>';
45831         
45832         
45833         var cindent = indent === false ? '' : (indent + '  ');
45834         // tags where we will not pad the children.. (inline text tags etc..)
45835         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45836             cindent = false;
45837             
45838             
45839         }
45840         
45841         var cn = this.cn(node, cindent );
45842         
45843         return ret + cn  + '</' + node.tagName + '>';
45844         
45845     },
45846     cn: function(node, indent)
45847     {
45848         var ret = [];
45849         
45850         var ar = Array.from(node.childNodes);
45851         for (var i = 0 ; i < ar.length ; i++) {
45852             
45853             
45854             
45855             if (indent !== false   // indent==false preservies everything
45856                 && i > 0
45857                 && ar[i].nodeType == 3 
45858                 && ar[i].nodeValue.length > 0
45859                 && ar[i].nodeValue.match(/^\s+/)
45860             ) {
45861                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45862                     ret.pop(); // remove line break from last?
45863                 }
45864                 
45865                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45866             }
45867             if (indent !== false
45868                 && ar[i].nodeType == 1 // element - and indent is not set... 
45869             ) {
45870                 ret.push("\n" + indent); 
45871             }
45872             
45873             ret.push(this.tidy(ar[i], indent));
45874             // text + trailing indent 
45875             if (indent !== false
45876                 && ar[i].nodeType == 3
45877                 && ar[i].nodeValue.length > 0
45878                 && ar[i].nodeValue.match(/\s+$/)
45879             ){
45880                 ret.push("\n" + indent); 
45881             }
45882             
45883             
45884             
45885             
45886         }
45887         // what if all text?
45888         
45889         
45890         return ret.join('');
45891     },
45892     
45893          
45894         
45895     attr : function(node)
45896     {
45897         var attr = [];
45898         for(i = 0; i < node.attributes.length;i++) {
45899             
45900             // skip empty values?
45901             if (!node.attributes.item(i).value.length) {
45902                 continue;
45903             }
45904             attr.push(  node.attributes.item(i).name + '="' +
45905                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45906             );
45907         }
45908         return attr.length ? (' ' + attr.join(' ') ) : '';
45909         
45910     }
45911     
45912     
45913     
45914 }
45915 /**
45916  * @class Roo.htmleditor.KeyEnter
45917  * Handle Enter press..
45918  * @cfg {Roo.HtmlEditorCore} core the editor.
45919  * @constructor
45920  * Create a new Filter.
45921  * @param {Object} config Configuration options
45922  */
45923
45924
45925
45926 Roo.htmleditor.KeyEnter = function(cfg) {
45927     Roo.apply(this, cfg);
45928     // this does not actually call walk as it's really just a abstract class
45929  
45930     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45931 }
45932
45933
45934 Roo.htmleditor.KeyEnter.prototype = {
45935     
45936     core : false,
45937     
45938     keypress : function(e) {
45939         if (e.charCode != 13) {
45940             return true;
45941         }
45942         e.preventDefault();
45943         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45944         var doc = this.core.doc;
45945         
45946         var docFragment = doc.createDocumentFragment();
45947     
45948         //add a new line
45949         var newEle = doc.createTextNode('\n');
45950         docFragment.appendChild(newEle);
45951     
45952     
45953         var range = this.core.win.getSelection().getRangeAt(0);
45954         var n = range.commonAncestorContainer ;
45955         while (n && n.nodeType != 1) {
45956             n  = n.parentNode;
45957         }
45958         var li = false;
45959         if (n && n.tagName == 'UL') {
45960             li = doc.createElement('LI');
45961             n.appendChild(li);
45962             
45963         }
45964         if (n && n.tagName == 'LI') {
45965             li = doc.createElement('LI');
45966             if (n.nextSibling) {
45967                 n.parentNode.insertBefore(li, n.firstSibling);
45968                 
45969             } else {
45970                 n.parentNode.appendChild(li);
45971             }
45972         }
45973         if (li) {   
45974             range = doc.createRange();
45975             range.setStartAfter(li);
45976             range.collapse(true);
45977         
45978             //make the cursor there
45979             var sel = this.core.win.getSelection();
45980             sel.removeAllRanges();
45981             sel.addRange(range);
45982             return false;
45983             
45984             
45985         }
45986         //add the br, or p, or something else
45987         newEle = doc.createElement('br');
45988         docFragment.appendChild(newEle);
45989     
45990         //make the br replace selection
45991         
45992         range.deleteContents();
45993         
45994         range.insertNode(docFragment);
45995     
45996         //create a new range
45997         range = doc.createRange();
45998         range.setStartAfter(newEle);
45999         range.collapse(true);
46000     
46001         //make the cursor there
46002         var sel = this.core.win.getSelection();
46003         sel.removeAllRanges();
46004         sel.addRange(range);
46005     
46006         return false;
46007          
46008     }
46009 };
46010      
46011 /**
46012  * @class Roo.htmleditor.Block
46013  * Base class for html editor blocks - do not use it directly .. extend it..
46014  * @cfg {DomElement} node The node to apply stuff to.
46015  * @cfg {String} friendly_name the name that appears in the context bar about this block
46016  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46017  
46018  * @constructor
46019  * Create a new Filter.
46020  * @param {Object} config Configuration options
46021  */
46022
46023 Roo.htmleditor.Block  = function(cfg)
46024 {
46025     // do nothing .. should not be called really.
46026 }
46027
46028 Roo.htmleditor.Block.factory = function(node)
46029 {
46030     
46031     var id = Roo.get(node).id;
46032     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46033         Roo.htmleditor.Block.cache[id].readElement();
46034         return Roo.htmleditor.Block.cache[id];
46035     }
46036     
46037     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46038     if (typeof(cls) == 'undefined') {
46039         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46040         return false;
46041     }
46042     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46043     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46044 };
46045 // question goes here... do we need to clear out this cache sometimes?
46046 // or show we make it relivant to the htmleditor.
46047 Roo.htmleditor.Block.cache = {};
46048
46049 Roo.htmleditor.Block.prototype = {
46050     
46051     node : false,
46052     
46053      // used by context menu
46054     friendly_name : 'Image with caption',
46055     
46056     context : false,
46057     /**
46058      * Update a node with values from this object
46059      * @param {DomElement} node
46060      */
46061     updateElement : function(node)
46062     {
46063         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46064     },
46065      /**
46066      * convert to plain HTML for calling insertAtCursor..
46067      */
46068     toHTML : function()
46069     {
46070         return Roo.DomHelper.markup(this.toObject());
46071     },
46072     /**
46073      * used by readEleemnt to extract data from a node
46074      * may need improving as it's pretty basic
46075      
46076      * @param {DomElement} node
46077      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46078      * @param {String} attribute (use html - for contents, or style for using next param as style)
46079      * @param {String} style the style property - eg. text-align
46080      */
46081     getVal : function(node, tag, attr, style)
46082     {
46083         var n = node;
46084         if (tag !== true && n.tagName != tag.toUpperCase()) {
46085             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46086             // but kiss for now.
46087             n = node.getElementsByTagName(tag).item(0);
46088         }
46089         if (attr == 'html') {
46090             return n.innerHTML;
46091         }
46092         if (attr == 'style') {
46093             return Roo.get(n).getStyle(style);
46094         }
46095         
46096         return Roo.get(n).attr(attr);
46097             
46098     },
46099     /**
46100      * create a DomHelper friendly object - for use with 
46101      * Roo.DomHelper.markup / overwrite / etc..
46102      * (override this)
46103      */
46104     toObject : function()
46105     {
46106         return {};
46107     },
46108       /**
46109      * Read a node that has a 'data-block' property - and extract the values from it.
46110      * @param {DomElement} node - the node
46111      */
46112     readElement : function(node)
46113     {
46114         
46115     } 
46116     
46117     
46118 };
46119
46120  
46121
46122 /**
46123  * @class Roo.htmleditor.BlockFigure
46124  * Block that has an image and a figcaption
46125  * @cfg {String} image_src the url for the image
46126  * @cfg {String} align (left|right) alignment for the block default left
46127  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46128  * @cfg {String} caption the text to appear below  (and in the alt tag)
46129  * @cfg {String|number} image_width the width of the image number or %?
46130  * @cfg {String|number} image_height the height of the image number or %?
46131  * 
46132  * @constructor
46133  * Create a new Filter.
46134  * @param {Object} config Configuration options
46135  */
46136
46137 Roo.htmleditor.BlockFigure = function(cfg)
46138 {
46139     if (cfg.node) {
46140         this.readElement(cfg.node);
46141         this.updateElement(cfg.node);
46142     }
46143     Roo.apply(this, cfg);
46144 }
46145 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46146  
46147     
46148     // setable values.
46149     image_src: '',
46150     
46151     align: 'left',
46152     caption : '',
46153     text_align: 'left',
46154     
46155     width : '46%',
46156     margin: '2%',
46157     
46158     // used by context menu
46159     friendly_name : 'Image with caption',
46160     
46161     context : { // ?? static really
46162         width : {
46163             title: "Width",
46164             width: 40
46165             // ?? number
46166         },
46167         margin : {
46168             title: "Margin",
46169             width: 40
46170             // ?? number
46171         },
46172         align: {
46173             title: "Align",
46174             opts : [[ "left"],[ "right"]],
46175             width : 80
46176             
46177         },
46178         text_align: {
46179             title: "Caption Align",
46180             opts : [ [ "left"],[ "right"],[ "center"]],
46181             width : 80
46182         },
46183         
46184        
46185         image_src : {
46186             title: "Src",
46187             width: 220
46188         }
46189     },
46190     /**
46191      * create a DomHelper friendly object - for use with
46192      * Roo.DomHelper.markup / overwrite / etc..
46193      */
46194     toObject : function()
46195     {
46196         var d = document.createElement('div');
46197         d.innerHTML = this.caption;
46198         
46199         return {
46200             tag: 'figure',
46201             'data-block' : 'Figure',
46202             contenteditable : 'false',
46203             style : {
46204                 display: 'table',
46205                 float :  this.align ,
46206                 width :  this.width,
46207                 margin:  this.margin
46208             },
46209             cn : [
46210                 {
46211                     tag : 'img',
46212                     src : this.image_src,
46213                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46214                     style: {
46215                         width: '100%'
46216                     }
46217                 },
46218                 {
46219                     tag: 'figcaption',
46220                     contenteditable : true,
46221                     style : {
46222                         'text-align': this.text_align
46223                     },
46224                     html : this.caption
46225                     
46226                 }
46227             ]
46228         };
46229     },
46230     
46231     readElement : function(node)
46232     {
46233         this.image_src = this.getVal(node, 'img', 'src');
46234         this.align = this.getVal(node, 'figure', 'style', 'float');
46235         this.caption = this.getVal(node, 'figcaption', 'html');
46236         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46237         this.width = this.getVal(node, 'figure', 'style', 'width');
46238         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46239         
46240     } 
46241     
46242   
46243    
46244      
46245     
46246     
46247     
46248     
46249 })
46250
46251 //<script type="text/javascript">
46252
46253 /*
46254  * Based  Ext JS Library 1.1.1
46255  * Copyright(c) 2006-2007, Ext JS, LLC.
46256  * LGPL
46257  *
46258  */
46259  
46260 /**
46261  * @class Roo.HtmlEditorCore
46262  * @extends Roo.Component
46263  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46264  *
46265  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46266  */
46267
46268 Roo.HtmlEditorCore = function(config){
46269     
46270     
46271     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46272     
46273     
46274     this.addEvents({
46275         /**
46276          * @event initialize
46277          * Fires when the editor is fully initialized (including the iframe)
46278          * @param {Roo.HtmlEditorCore} this
46279          */
46280         initialize: true,
46281         /**
46282          * @event activate
46283          * Fires when the editor is first receives the focus. Any insertion must wait
46284          * until after this event.
46285          * @param {Roo.HtmlEditorCore} this
46286          */
46287         activate: true,
46288          /**
46289          * @event beforesync
46290          * Fires before the textarea is updated with content from the editor iframe. Return false
46291          * to cancel the sync.
46292          * @param {Roo.HtmlEditorCore} this
46293          * @param {String} html
46294          */
46295         beforesync: true,
46296          /**
46297          * @event beforepush
46298          * Fires before the iframe editor is updated with content from the textarea. Return false
46299          * to cancel the push.
46300          * @param {Roo.HtmlEditorCore} this
46301          * @param {String} html
46302          */
46303         beforepush: true,
46304          /**
46305          * @event sync
46306          * Fires when the textarea is updated with content from the editor iframe.
46307          * @param {Roo.HtmlEditorCore} this
46308          * @param {String} html
46309          */
46310         sync: true,
46311          /**
46312          * @event push
46313          * Fires when the iframe editor is updated with content from the textarea.
46314          * @param {Roo.HtmlEditorCore} this
46315          * @param {String} html
46316          */
46317         push: true,
46318         
46319         /**
46320          * @event editorevent
46321          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46322          * @param {Roo.HtmlEditorCore} this
46323          */
46324         editorevent: true
46325         
46326     });
46327     
46328     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46329     
46330     // defaults : white / black...
46331     this.applyBlacklists();
46332     
46333     
46334     
46335 };
46336
46337
46338 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46339
46340
46341      /**
46342      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46343      */
46344     
46345     owner : false,
46346     
46347      /**
46348      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46349      *                        Roo.resizable.
46350      */
46351     resizable : false,
46352      /**
46353      * @cfg {Number} height (in pixels)
46354      */   
46355     height: 300,
46356    /**
46357      * @cfg {Number} width (in pixels)
46358      */   
46359     width: 500,
46360     
46361     /**
46362      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46363      * 
46364      */
46365     stylesheets: false,
46366     
46367     /**
46368      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46369      */
46370     allowComments: false,
46371     // id of frame..
46372     frameId: false,
46373     
46374     // private properties
46375     validationEvent : false,
46376     deferHeight: true,
46377     initialized : false,
46378     activated : false,
46379     sourceEditMode : false,
46380     onFocus : Roo.emptyFn,
46381     iframePad:3,
46382     hideMode:'offsets',
46383     
46384     clearUp: true,
46385     
46386     // blacklist + whitelisted elements..
46387     black: false,
46388     white: false,
46389      
46390     bodyCls : '',
46391
46392     /**
46393      * Protected method that will not generally be called directly. It
46394      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46395      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46396      */
46397     getDocMarkup : function(){
46398         // body styles..
46399         var st = '';
46400         
46401         // inherit styels from page...?? 
46402         if (this.stylesheets === false) {
46403             
46404             Roo.get(document.head).select('style').each(function(node) {
46405                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46406             });
46407             
46408             Roo.get(document.head).select('link').each(function(node) { 
46409                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46410             });
46411             
46412         } else if (!this.stylesheets.length) {
46413                 // simple..
46414                 st = '<style type="text/css">' +
46415                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46416                    '</style>';
46417         } else {
46418             for (var i in this.stylesheets) {
46419                 if (typeof(this.stylesheets[i]) != 'string') {
46420                     continue;
46421                 }
46422                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46423             }
46424             
46425         }
46426         
46427         st +=  '<style type="text/css">' +
46428             'IMG { cursor: pointer } ' +
46429         '</style>';
46430
46431         var cls = 'roo-htmleditor-body';
46432         
46433         if(this.bodyCls.length){
46434             cls += ' ' + this.bodyCls;
46435         }
46436         
46437         return '<html><head>' + st  +
46438             //<style type="text/css">' +
46439             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46440             //'</style>' +
46441             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46442     },
46443
46444     // private
46445     onRender : function(ct, position)
46446     {
46447         var _t = this;
46448         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46449         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46450         
46451         
46452         this.el.dom.style.border = '0 none';
46453         this.el.dom.setAttribute('tabIndex', -1);
46454         this.el.addClass('x-hidden hide');
46455         
46456         
46457         
46458         if(Roo.isIE){ // fix IE 1px bogus margin
46459             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46460         }
46461        
46462         
46463         this.frameId = Roo.id();
46464         
46465          
46466         
46467         var iframe = this.owner.wrap.createChild({
46468             tag: 'iframe',
46469             cls: 'form-control', // bootstrap..
46470             id: this.frameId,
46471             name: this.frameId,
46472             frameBorder : 'no',
46473             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46474         }, this.el
46475         );
46476         
46477         
46478         this.iframe = iframe.dom;
46479
46480         this.assignDocWin();
46481         
46482         this.doc.designMode = 'on';
46483        
46484         this.doc.open();
46485         this.doc.write(this.getDocMarkup());
46486         this.doc.close();
46487
46488         
46489         var task = { // must defer to wait for browser to be ready
46490             run : function(){
46491                 //console.log("run task?" + this.doc.readyState);
46492                 this.assignDocWin();
46493                 if(this.doc.body || this.doc.readyState == 'complete'){
46494                     try {
46495                         this.doc.designMode="on";
46496                     } catch (e) {
46497                         return;
46498                     }
46499                     Roo.TaskMgr.stop(task);
46500                     this.initEditor.defer(10, this);
46501                 }
46502             },
46503             interval : 10,
46504             duration: 10000,
46505             scope: this
46506         };
46507         Roo.TaskMgr.start(task);
46508
46509     },
46510
46511     // private
46512     onResize : function(w, h)
46513     {
46514          Roo.log('resize: ' +w + ',' + h );
46515         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46516         if(!this.iframe){
46517             return;
46518         }
46519         if(typeof w == 'number'){
46520             
46521             this.iframe.style.width = w + 'px';
46522         }
46523         if(typeof h == 'number'){
46524             
46525             this.iframe.style.height = h + 'px';
46526             if(this.doc){
46527                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46528             }
46529         }
46530         
46531     },
46532
46533     /**
46534      * Toggles the editor between standard and source edit mode.
46535      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46536      */
46537     toggleSourceEdit : function(sourceEditMode){
46538         
46539         this.sourceEditMode = sourceEditMode === true;
46540         
46541         if(this.sourceEditMode){
46542  
46543             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46544             
46545         }else{
46546             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46547             //this.iframe.className = '';
46548             this.deferFocus();
46549         }
46550         //this.setSize(this.owner.wrap.getSize());
46551         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46552     },
46553
46554     
46555   
46556
46557     /**
46558      * Protected method that will not generally be called directly. If you need/want
46559      * custom HTML cleanup, this is the method you should override.
46560      * @param {String} html The HTML to be cleaned
46561      * return {String} The cleaned HTML
46562      */
46563     cleanHtml : function(html){
46564         html = String(html);
46565         if(html.length > 5){
46566             if(Roo.isSafari){ // strip safari nonsense
46567                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46568             }
46569         }
46570         if(html == '&nbsp;'){
46571             html = '';
46572         }
46573         return html;
46574     },
46575
46576     /**
46577      * HTML Editor -> Textarea
46578      * Protected method that will not generally be called directly. Syncs the contents
46579      * of the editor iframe with the textarea.
46580      */
46581     syncValue : function()
46582     {
46583         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46584         if(this.initialized){
46585             var bd = (this.doc.body || this.doc.documentElement);
46586             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46587             
46588             // not sure if this is really the place for this
46589             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46590             // this has to update attributes that get duped.. like alt and caption..
46591             
46592             
46593             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46594             //     Roo.htmleditor.Block.factory(e);
46595             //},this);
46596             
46597             
46598             var div = document.createElement('div');
46599             div.innerHTML = bd.innerHTML;
46600             // remove content editable. (blocks)
46601             
46602            
46603             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46604             //?? tidy?
46605             var html = div.innerHTML;
46606             if(Roo.isSafari){
46607                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46608                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46609                 if(m && m[1]){
46610                     html = '<div style="'+m[0]+'">' + html + '</div>';
46611                 }
46612             }
46613             html = this.cleanHtml(html);
46614             // fix up the special chars.. normaly like back quotes in word...
46615             // however we do not want to do this with chinese..
46616             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46617                 
46618                 var cc = match.charCodeAt();
46619
46620                 // Get the character value, handling surrogate pairs
46621                 if (match.length == 2) {
46622                     // It's a surrogate pair, calculate the Unicode code point
46623                     var high = match.charCodeAt(0) - 0xD800;
46624                     var low  = match.charCodeAt(1) - 0xDC00;
46625                     cc = (high * 0x400) + low + 0x10000;
46626                 }  else if (
46627                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46628                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46629                     (cc >= 0xf900 && cc < 0xfb00 )
46630                 ) {
46631                         return match;
46632                 }  
46633          
46634                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46635                 return "&#" + cc + ";";
46636                 
46637                 
46638             });
46639             
46640             
46641              
46642             if(this.owner.fireEvent('beforesync', this, html) !== false){
46643                 this.el.dom.value = html;
46644                 this.owner.fireEvent('sync', this, html);
46645             }
46646         }
46647     },
46648
46649     /**
46650      * TEXTAREA -> EDITABLE
46651      * Protected method that will not generally be called directly. Pushes the value of the textarea
46652      * into the iframe editor.
46653      */
46654     pushValue : function()
46655     {
46656         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46657         if(this.initialized){
46658             var v = this.el.dom.value.trim();
46659             
46660             
46661             if(this.owner.fireEvent('beforepush', this, v) !== false){
46662                 var d = (this.doc.body || this.doc.documentElement);
46663                 d.innerHTML = v;
46664                  
46665                 this.el.dom.value = d.innerHTML;
46666                 this.owner.fireEvent('push', this, v);
46667             }
46668             
46669             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46670                 
46671                 Roo.htmleditor.Block.factory(e);
46672                 
46673             },this);
46674             var lc = this.doc.body.lastChild;
46675             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46676                 // add an extra line at the end.
46677                 this.doc.body.appendChild(this.doc.createElement('br'));
46678             }
46679             
46680             
46681         }
46682     },
46683
46684     // private
46685     deferFocus : function(){
46686         this.focus.defer(10, this);
46687     },
46688
46689     // doc'ed in Field
46690     focus : function(){
46691         if(this.win && !this.sourceEditMode){
46692             this.win.focus();
46693         }else{
46694             this.el.focus();
46695         }
46696     },
46697     
46698     assignDocWin: function()
46699     {
46700         var iframe = this.iframe;
46701         
46702          if(Roo.isIE){
46703             this.doc = iframe.contentWindow.document;
46704             this.win = iframe.contentWindow;
46705         } else {
46706 //            if (!Roo.get(this.frameId)) {
46707 //                return;
46708 //            }
46709 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46710 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46711             
46712             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46713                 return;
46714             }
46715             
46716             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46717             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46718         }
46719     },
46720     
46721     // private
46722     initEditor : function(){
46723         //console.log("INIT EDITOR");
46724         this.assignDocWin();
46725         
46726         
46727         
46728         this.doc.designMode="on";
46729         this.doc.open();
46730         this.doc.write(this.getDocMarkup());
46731         this.doc.close();
46732         
46733         var dbody = (this.doc.body || this.doc.documentElement);
46734         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46735         // this copies styles from the containing element into thsi one..
46736         // not sure why we need all of this..
46737         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46738         
46739         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46740         //ss['background-attachment'] = 'fixed'; // w3c
46741         dbody.bgProperties = 'fixed'; // ie
46742         //Roo.DomHelper.applyStyles(dbody, ss);
46743         Roo.EventManager.on(this.doc, {
46744             //'mousedown': this.onEditorEvent,
46745             'mouseup': this.onEditorEvent,
46746             'dblclick': this.onEditorEvent,
46747             'click': this.onEditorEvent,
46748             'keyup': this.onEditorEvent,
46749             
46750             buffer:100,
46751             scope: this
46752         });
46753         Roo.EventManager.on(this.doc, {
46754             'paste': this.onPasteEvent,
46755             scope : this
46756         });
46757         if(Roo.isGecko){
46758             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46759         }
46760         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46761             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46762         }
46763         this.initialized = true;
46764
46765         
46766         // initialize special key events - enter
46767         new Roo.htmleditor.KeyEnter({core : this});
46768         
46769          
46770         
46771         this.owner.fireEvent('initialize', this);
46772         this.pushValue();
46773     },
46774     
46775     onPasteEvent : function(e,v)
46776     {
46777         // I think we better assume paste is going to be a dirty load of rubish from word..
46778         
46779         // even pasting into a 'email version' of this widget will have to clean up that mess.
46780         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46781         
46782         // check what type of paste - if it's an image, then handle it differently.
46783         if (cd.files.length > 0) {
46784             // pasting images?
46785             var urlAPI = (window.createObjectURL && window) || 
46786                 (window.URL && URL.revokeObjectURL && URL) || 
46787                 (window.webkitURL && webkitURL);
46788     
46789             var url = urlAPI.createObjectURL( cd.files[0]);
46790             this.insertAtCursor('<img src=" + url + ">');
46791             return false;
46792         }
46793         
46794         var html = cd.getData('text/html'); // clipboard event
46795         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46796         var images = parser.doc.getElementsByType('pict');
46797         Roo.log(images);
46798         //Roo.log(imgs);
46799         // fixme..
46800         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46801                        .map(function(g) { return g.toDataURL(); });
46802         
46803         
46804         html = this.cleanWordChars(html);
46805         
46806         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46807         
46808         if (images.length > 0) {
46809             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46810                 img.setAttribute('src', images[i]);
46811             });
46812         }
46813         
46814       
46815         new Roo.htmleditor.FilterStyleToTag({ node : d });
46816         new Roo.htmleditor.FilterAttributes({
46817             node : d,
46818             attrib_white : ['href', 'src', 'name', 'align'],
46819             attrib_clean : ['href', 'src' ] 
46820         });
46821         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46822         // should be fonts..
46823         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46824         new Roo.htmleditor.FilterParagraph({ node : d });
46825         new Roo.htmleditor.FilterSpan({ node : d });
46826         new Roo.htmleditor.FilterLongBr({ node : d });
46827         
46828         
46829         
46830         this.insertAtCursor(d.innerHTML);
46831         
46832         e.preventDefault();
46833         return false;
46834         // default behaveiour should be our local cleanup paste? (optional?)
46835         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46836         //this.owner.fireEvent('paste', e, v);
46837     },
46838     // private
46839     onDestroy : function(){
46840         
46841         
46842         
46843         if(this.rendered){
46844             
46845             //for (var i =0; i < this.toolbars.length;i++) {
46846             //    // fixme - ask toolbars for heights?
46847             //    this.toolbars[i].onDestroy();
46848            // }
46849             
46850             //this.wrap.dom.innerHTML = '';
46851             //this.wrap.remove();
46852         }
46853     },
46854
46855     // private
46856     onFirstFocus : function(){
46857         
46858         this.assignDocWin();
46859         
46860         
46861         this.activated = true;
46862          
46863     
46864         if(Roo.isGecko){ // prevent silly gecko errors
46865             this.win.focus();
46866             var s = this.win.getSelection();
46867             if(!s.focusNode || s.focusNode.nodeType != 3){
46868                 var r = s.getRangeAt(0);
46869                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46870                 r.collapse(true);
46871                 this.deferFocus();
46872             }
46873             try{
46874                 this.execCmd('useCSS', true);
46875                 this.execCmd('styleWithCSS', false);
46876             }catch(e){}
46877         }
46878         this.owner.fireEvent('activate', this);
46879     },
46880
46881     // private
46882     adjustFont: function(btn){
46883         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46884         //if(Roo.isSafari){ // safari
46885         //    adjust *= 2;
46886        // }
46887         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46888         if(Roo.isSafari){ // safari
46889             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46890             v =  (v < 10) ? 10 : v;
46891             v =  (v > 48) ? 48 : v;
46892             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46893             
46894         }
46895         
46896         
46897         v = Math.max(1, v+adjust);
46898         
46899         this.execCmd('FontSize', v  );
46900     },
46901
46902     onEditorEvent : function(e)
46903     {
46904         this.owner.fireEvent('editorevent', this, e);
46905       //  this.updateToolbar();
46906         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46907     },
46908
46909     insertTag : function(tg)
46910     {
46911         // could be a bit smarter... -> wrap the current selected tRoo..
46912         if (tg.toLowerCase() == 'span' ||
46913             tg.toLowerCase() == 'code' ||
46914             tg.toLowerCase() == 'sup' ||
46915             tg.toLowerCase() == 'sub' 
46916             ) {
46917             
46918             range = this.createRange(this.getSelection());
46919             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46920             wrappingNode.appendChild(range.extractContents());
46921             range.insertNode(wrappingNode);
46922
46923             return;
46924             
46925             
46926             
46927         }
46928         this.execCmd("formatblock",   tg);
46929         
46930     },
46931     
46932     insertText : function(txt)
46933     {
46934         
46935         
46936         var range = this.createRange();
46937         range.deleteContents();
46938                //alert(Sender.getAttribute('label'));
46939                
46940         range.insertNode(this.doc.createTextNode(txt));
46941     } ,
46942     
46943      
46944
46945     /**
46946      * Executes a Midas editor command on the editor document and performs necessary focus and
46947      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46948      * @param {String} cmd The Midas command
46949      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46950      */
46951     relayCmd : function(cmd, value){
46952         this.win.focus();
46953         this.execCmd(cmd, value);
46954         this.owner.fireEvent('editorevent', this);
46955         //this.updateToolbar();
46956         this.owner.deferFocus();
46957     },
46958
46959     /**
46960      * Executes a Midas editor command directly on the editor document.
46961      * For visual commands, you should use {@link #relayCmd} instead.
46962      * <b>This should only be called after the editor is initialized.</b>
46963      * @param {String} cmd The Midas command
46964      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46965      */
46966     execCmd : function(cmd, value){
46967         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46968         this.syncValue();
46969     },
46970  
46971  
46972    
46973     /**
46974      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46975      * to insert tRoo.
46976      * @param {String} text | dom node.. 
46977      */
46978     insertAtCursor : function(text)
46979     {
46980         
46981         if(!this.activated){
46982             return;
46983         }
46984          
46985         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46986             this.win.focus();
46987             
46988             
46989             // from jquery ui (MIT licenced)
46990             var range, node;
46991             var win = this.win;
46992             
46993             if (win.getSelection && win.getSelection().getRangeAt) {
46994                 
46995                 // delete the existing?
46996                 
46997                 this.createRange(this.getSelection()).deleteContents();
46998                 range = win.getSelection().getRangeAt(0);
46999                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47000                 range.insertNode(node);
47001             } else if (win.document.selection && win.document.selection.createRange) {
47002                 // no firefox support
47003                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47004                 win.document.selection.createRange().pasteHTML(txt);
47005             } else {
47006                 // no firefox support
47007                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47008                 this.execCmd('InsertHTML', txt);
47009             } 
47010             
47011             this.syncValue();
47012             
47013             this.deferFocus();
47014         }
47015     },
47016  // private
47017     mozKeyPress : function(e){
47018         if(e.ctrlKey){
47019             var c = e.getCharCode(), cmd;
47020           
47021             if(c > 0){
47022                 c = String.fromCharCode(c).toLowerCase();
47023                 switch(c){
47024                     case 'b':
47025                         cmd = 'bold';
47026                         break;
47027                     case 'i':
47028                         cmd = 'italic';
47029                         break;
47030                     
47031                     case 'u':
47032                         cmd = 'underline';
47033                         break;
47034                     
47035                     //case 'v':
47036                       //  this.cleanUpPaste.defer(100, this);
47037                       //  return;
47038                         
47039                 }
47040                 if(cmd){
47041                     this.win.focus();
47042                     this.execCmd(cmd);
47043                     this.deferFocus();
47044                     e.preventDefault();
47045                 }
47046                 
47047             }
47048         }
47049     },
47050
47051     // private
47052     fixKeys : function(){ // load time branching for fastest keydown performance
47053         if(Roo.isIE){
47054             return function(e){
47055                 var k = e.getKey(), r;
47056                 if(k == e.TAB){
47057                     e.stopEvent();
47058                     r = this.doc.selection.createRange();
47059                     if(r){
47060                         r.collapse(true);
47061                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47062                         this.deferFocus();
47063                     }
47064                     return;
47065                 }
47066                 
47067                 if(k == e.ENTER){
47068                     r = this.doc.selection.createRange();
47069                     if(r){
47070                         var target = r.parentElement();
47071                         if(!target || target.tagName.toLowerCase() != 'li'){
47072                             e.stopEvent();
47073                             r.pasteHTML('<br/>');
47074                             r.collapse(false);
47075                             r.select();
47076                         }
47077                     }
47078                 }
47079                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47080                 //    this.cleanUpPaste.defer(100, this);
47081                 //    return;
47082                 //}
47083                 
47084                 
47085             };
47086         }else if(Roo.isOpera){
47087             return function(e){
47088                 var k = e.getKey();
47089                 if(k == e.TAB){
47090                     e.stopEvent();
47091                     this.win.focus();
47092                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47093                     this.deferFocus();
47094                 }
47095                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47096                 //    this.cleanUpPaste.defer(100, this);
47097                  //   return;
47098                 //}
47099                 
47100             };
47101         }else if(Roo.isSafari){
47102             return function(e){
47103                 var k = e.getKey();
47104                 
47105                 if(k == e.TAB){
47106                     e.stopEvent();
47107                     this.execCmd('InsertText','\t');
47108                     this.deferFocus();
47109                     return;
47110                 }
47111                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47112                  //   this.cleanUpPaste.defer(100, this);
47113                  //   return;
47114                // }
47115                 
47116              };
47117         }
47118     }(),
47119     
47120     getAllAncestors: function()
47121     {
47122         var p = this.getSelectedNode();
47123         var a = [];
47124         if (!p) {
47125             a.push(p); // push blank onto stack..
47126             p = this.getParentElement();
47127         }
47128         
47129         
47130         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47131             a.push(p);
47132             p = p.parentNode;
47133         }
47134         a.push(this.doc.body);
47135         return a;
47136     },
47137     lastSel : false,
47138     lastSelNode : false,
47139     
47140     
47141     getSelection : function() 
47142     {
47143         this.assignDocWin();
47144         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47145     },
47146     /**
47147      * Select a dom node
47148      * @param {DomElement} node the node to select
47149      */
47150     selectNode : function(node)
47151     {
47152         
47153             var nodeRange = node.ownerDocument.createRange();
47154             try {
47155                 nodeRange.selectNode(node);
47156             } catch (e) {
47157                 nodeRange.selectNodeContents(node);
47158             }
47159             //nodeRange.collapse(true);
47160             var s = this.win.getSelection();
47161             s.removeAllRanges();
47162             s.addRange(nodeRange);
47163     },
47164     
47165     getSelectedNode: function() 
47166     {
47167         // this may only work on Gecko!!!
47168         
47169         // should we cache this!!!!
47170         
47171         
47172         
47173          
47174         var range = this.createRange(this.getSelection()).cloneRange();
47175         
47176         if (Roo.isIE) {
47177             var parent = range.parentElement();
47178             while (true) {
47179                 var testRange = range.duplicate();
47180                 testRange.moveToElementText(parent);
47181                 if (testRange.inRange(range)) {
47182                     break;
47183                 }
47184                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47185                     break;
47186                 }
47187                 parent = parent.parentElement;
47188             }
47189             return parent;
47190         }
47191         
47192         // is ancestor a text element.
47193         var ac =  range.commonAncestorContainer;
47194         if (ac.nodeType == 3) {
47195             ac = ac.parentNode;
47196         }
47197         
47198         var ar = ac.childNodes;
47199          
47200         var nodes = [];
47201         var other_nodes = [];
47202         var has_other_nodes = false;
47203         for (var i=0;i<ar.length;i++) {
47204             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47205                 continue;
47206             }
47207             // fullly contained node.
47208             
47209             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47210                 nodes.push(ar[i]);
47211                 continue;
47212             }
47213             
47214             // probably selected..
47215             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47216                 other_nodes.push(ar[i]);
47217                 continue;
47218             }
47219             // outer..
47220             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47221                 continue;
47222             }
47223             
47224             
47225             has_other_nodes = true;
47226         }
47227         if (!nodes.length && other_nodes.length) {
47228             nodes= other_nodes;
47229         }
47230         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47231             return false;
47232         }
47233         
47234         return nodes[0];
47235     },
47236     createRange: function(sel)
47237     {
47238         // this has strange effects when using with 
47239         // top toolbar - not sure if it's a great idea.
47240         //this.editor.contentWindow.focus();
47241         if (typeof sel != "undefined") {
47242             try {
47243                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47244             } catch(e) {
47245                 return this.doc.createRange();
47246             }
47247         } else {
47248             return this.doc.createRange();
47249         }
47250     },
47251     getParentElement: function()
47252     {
47253         
47254         this.assignDocWin();
47255         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47256         
47257         var range = this.createRange(sel);
47258          
47259         try {
47260             var p = range.commonAncestorContainer;
47261             while (p.nodeType == 3) { // text node
47262                 p = p.parentNode;
47263             }
47264             return p;
47265         } catch (e) {
47266             return null;
47267         }
47268     
47269     },
47270     /***
47271      *
47272      * Range intersection.. the hard stuff...
47273      *  '-1' = before
47274      *  '0' = hits..
47275      *  '1' = after.
47276      *         [ -- selected range --- ]
47277      *   [fail]                        [fail]
47278      *
47279      *    basically..
47280      *      if end is before start or  hits it. fail.
47281      *      if start is after end or hits it fail.
47282      *
47283      *   if either hits (but other is outside. - then it's not 
47284      *   
47285      *    
47286      **/
47287     
47288     
47289     // @see http://www.thismuchiknow.co.uk/?p=64.
47290     rangeIntersectsNode : function(range, node)
47291     {
47292         var nodeRange = node.ownerDocument.createRange();
47293         try {
47294             nodeRange.selectNode(node);
47295         } catch (e) {
47296             nodeRange.selectNodeContents(node);
47297         }
47298     
47299         var rangeStartRange = range.cloneRange();
47300         rangeStartRange.collapse(true);
47301     
47302         var rangeEndRange = range.cloneRange();
47303         rangeEndRange.collapse(false);
47304     
47305         var nodeStartRange = nodeRange.cloneRange();
47306         nodeStartRange.collapse(true);
47307     
47308         var nodeEndRange = nodeRange.cloneRange();
47309         nodeEndRange.collapse(false);
47310     
47311         return rangeStartRange.compareBoundaryPoints(
47312                  Range.START_TO_START, nodeEndRange) == -1 &&
47313                rangeEndRange.compareBoundaryPoints(
47314                  Range.START_TO_START, nodeStartRange) == 1;
47315         
47316          
47317     },
47318     rangeCompareNode : function(range, node)
47319     {
47320         var nodeRange = node.ownerDocument.createRange();
47321         try {
47322             nodeRange.selectNode(node);
47323         } catch (e) {
47324             nodeRange.selectNodeContents(node);
47325         }
47326         
47327         
47328         range.collapse(true);
47329     
47330         nodeRange.collapse(true);
47331      
47332         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47333         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47334          
47335         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47336         
47337         var nodeIsBefore   =  ss == 1;
47338         var nodeIsAfter    = ee == -1;
47339         
47340         if (nodeIsBefore && nodeIsAfter) {
47341             return 0; // outer
47342         }
47343         if (!nodeIsBefore && nodeIsAfter) {
47344             return 1; //right trailed.
47345         }
47346         
47347         if (nodeIsBefore && !nodeIsAfter) {
47348             return 2;  // left trailed.
47349         }
47350         // fully contined.
47351         return 3;
47352     },
47353  
47354     cleanWordChars : function(input) {// change the chars to hex code
47355         
47356        var swapCodes  = [ 
47357             [    8211, "&#8211;" ], 
47358             [    8212, "&#8212;" ], 
47359             [    8216,  "'" ],  
47360             [    8217, "'" ],  
47361             [    8220, '"' ],  
47362             [    8221, '"' ],  
47363             [    8226, "*" ],  
47364             [    8230, "..." ]
47365         ]; 
47366         var output = input;
47367         Roo.each(swapCodes, function(sw) { 
47368             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47369             
47370             output = output.replace(swapper, sw[1]);
47371         });
47372         
47373         return output;
47374     },
47375     
47376      
47377     
47378         
47379     
47380     cleanUpChild : function (node)
47381     {
47382         
47383         new Roo.htmleditor.FilterComment({node : node});
47384         new Roo.htmleditor.FilterAttributes({
47385                 node : node,
47386                 attrib_black : this.ablack,
47387                 attrib_clean : this.aclean,
47388                 style_white : this.cwhite,
47389                 style_black : this.cblack
47390         });
47391         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47392         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47393          
47394         
47395     },
47396     
47397     /**
47398      * Clean up MS wordisms...
47399      * @deprecated - use filter directly
47400      */
47401     cleanWord : function(node)
47402     {
47403         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47404         
47405     },
47406    
47407     
47408     /**
47409
47410      * @deprecated - use filters
47411      */
47412     cleanTableWidths : function(node)
47413     {
47414         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47415         
47416  
47417     },
47418     
47419      
47420         
47421     applyBlacklists : function()
47422     {
47423         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47424         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47425         
47426         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47427         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47428         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47429         
47430         this.white = [];
47431         this.black = [];
47432         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47433             if (b.indexOf(tag) > -1) {
47434                 return;
47435             }
47436             this.white.push(tag);
47437             
47438         }, this);
47439         
47440         Roo.each(w, function(tag) {
47441             if (b.indexOf(tag) > -1) {
47442                 return;
47443             }
47444             if (this.white.indexOf(tag) > -1) {
47445                 return;
47446             }
47447             this.white.push(tag);
47448             
47449         }, this);
47450         
47451         
47452         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47453             if (w.indexOf(tag) > -1) {
47454                 return;
47455             }
47456             this.black.push(tag);
47457             
47458         }, this);
47459         
47460         Roo.each(b, function(tag) {
47461             if (w.indexOf(tag) > -1) {
47462                 return;
47463             }
47464             if (this.black.indexOf(tag) > -1) {
47465                 return;
47466             }
47467             this.black.push(tag);
47468             
47469         }, this);
47470         
47471         
47472         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47473         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47474         
47475         this.cwhite = [];
47476         this.cblack = [];
47477         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47478             if (b.indexOf(tag) > -1) {
47479                 return;
47480             }
47481             this.cwhite.push(tag);
47482             
47483         }, this);
47484         
47485         Roo.each(w, function(tag) {
47486             if (b.indexOf(tag) > -1) {
47487                 return;
47488             }
47489             if (this.cwhite.indexOf(tag) > -1) {
47490                 return;
47491             }
47492             this.cwhite.push(tag);
47493             
47494         }, this);
47495         
47496         
47497         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47498             if (w.indexOf(tag) > -1) {
47499                 return;
47500             }
47501             this.cblack.push(tag);
47502             
47503         }, this);
47504         
47505         Roo.each(b, function(tag) {
47506             if (w.indexOf(tag) > -1) {
47507                 return;
47508             }
47509             if (this.cblack.indexOf(tag) > -1) {
47510                 return;
47511             }
47512             this.cblack.push(tag);
47513             
47514         }, this);
47515     },
47516     
47517     setStylesheets : function(stylesheets)
47518     {
47519         if(typeof(stylesheets) == 'string'){
47520             Roo.get(this.iframe.contentDocument.head).createChild({
47521                 tag : 'link',
47522                 rel : 'stylesheet',
47523                 type : 'text/css',
47524                 href : stylesheets
47525             });
47526             
47527             return;
47528         }
47529         var _this = this;
47530      
47531         Roo.each(stylesheets, function(s) {
47532             if(!s.length){
47533                 return;
47534             }
47535             
47536             Roo.get(_this.iframe.contentDocument.head).createChild({
47537                 tag : 'link',
47538                 rel : 'stylesheet',
47539                 type : 'text/css',
47540                 href : s
47541             });
47542         });
47543
47544         
47545     },
47546     
47547     removeStylesheets : function()
47548     {
47549         var _this = this;
47550         
47551         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47552             s.remove();
47553         });
47554     },
47555     
47556     setStyle : function(style)
47557     {
47558         Roo.get(this.iframe.contentDocument.head).createChild({
47559             tag : 'style',
47560             type : 'text/css',
47561             html : style
47562         });
47563
47564         return;
47565     }
47566     
47567     // hide stuff that is not compatible
47568     /**
47569      * @event blur
47570      * @hide
47571      */
47572     /**
47573      * @event change
47574      * @hide
47575      */
47576     /**
47577      * @event focus
47578      * @hide
47579      */
47580     /**
47581      * @event specialkey
47582      * @hide
47583      */
47584     /**
47585      * @cfg {String} fieldClass @hide
47586      */
47587     /**
47588      * @cfg {String} focusClass @hide
47589      */
47590     /**
47591      * @cfg {String} autoCreate @hide
47592      */
47593     /**
47594      * @cfg {String} inputType @hide
47595      */
47596     /**
47597      * @cfg {String} invalidClass @hide
47598      */
47599     /**
47600      * @cfg {String} invalidText @hide
47601      */
47602     /**
47603      * @cfg {String} msgFx @hide
47604      */
47605     /**
47606      * @cfg {String} validateOnBlur @hide
47607      */
47608 });
47609
47610 Roo.HtmlEditorCore.white = [
47611         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47612         
47613        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47614        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47615        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47616        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47617        'TABLE',   'UL',         'XMP', 
47618        
47619        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47620       'THEAD',   'TR', 
47621      
47622       'DIR', 'MENU', 'OL', 'UL', 'DL',
47623        
47624       'EMBED',  'OBJECT'
47625 ];
47626
47627
47628 Roo.HtmlEditorCore.black = [
47629     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47630         'APPLET', // 
47631         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47632         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47633         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47634         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47635         //'FONT' // CLEAN LATER..
47636         'COLGROUP', 'COL'  // messy tables.
47637         
47638 ];
47639 Roo.HtmlEditorCore.clean = [ // ?? needed???
47640      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47641 ];
47642 Roo.HtmlEditorCore.tag_remove = [
47643     'FONT', 'TBODY'  
47644 ];
47645 // attributes..
47646
47647 Roo.HtmlEditorCore.ablack = [
47648     'on'
47649 ];
47650     
47651 Roo.HtmlEditorCore.aclean = [ 
47652     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47653 ];
47654
47655 // protocols..
47656 Roo.HtmlEditorCore.pwhite= [
47657         'http',  'https',  'mailto'
47658 ];
47659
47660 // white listed style attributes.
47661 Roo.HtmlEditorCore.cwhite= [
47662       //  'text-align', /// default is to allow most things..
47663       
47664          
47665 //        'font-size'//??
47666 ];
47667
47668 // black listed style attributes.
47669 Roo.HtmlEditorCore.cblack= [
47670       //  'font-size' -- this can be set by the project 
47671 ];
47672
47673
47674
47675
47676     //<script type="text/javascript">
47677
47678 /*
47679  * Ext JS Library 1.1.1
47680  * Copyright(c) 2006-2007, Ext JS, LLC.
47681  * Licence LGPL
47682  * 
47683  */
47684  
47685  
47686 Roo.form.HtmlEditor = function(config){
47687     
47688     
47689     
47690     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47691     
47692     if (!this.toolbars) {
47693         this.toolbars = [];
47694     }
47695     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47696     
47697     
47698 };
47699
47700 /**
47701  * @class Roo.form.HtmlEditor
47702  * @extends Roo.form.Field
47703  * Provides a lightweight HTML Editor component.
47704  *
47705  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47706  * 
47707  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47708  * supported by this editor.</b><br/><br/>
47709  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47710  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47711  */
47712 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47713     /**
47714      * @cfg {Boolean} clearUp
47715      */
47716     clearUp : true,
47717       /**
47718      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47719      */
47720     toolbars : false,
47721    
47722      /**
47723      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47724      *                        Roo.resizable.
47725      */
47726     resizable : false,
47727      /**
47728      * @cfg {Number} height (in pixels)
47729      */   
47730     height: 300,
47731    /**
47732      * @cfg {Number} width (in pixels)
47733      */   
47734     width: 500,
47735     
47736     /**
47737      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47738      * 
47739      */
47740     stylesheets: false,
47741     
47742     
47743      /**
47744      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47745      * 
47746      */
47747     cblack: false,
47748     /**
47749      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47750      * 
47751      */
47752     cwhite: false,
47753     
47754      /**
47755      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47756      * 
47757      */
47758     black: false,
47759     /**
47760      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47761      * 
47762      */
47763     white: false,
47764     /**
47765      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47766      */
47767     allowComments: false,
47768     /**
47769      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47770      */
47771     
47772     
47773      bodyCls : '',
47774     
47775     // id of frame..
47776     frameId: false,
47777     
47778     // private properties
47779     validationEvent : false,
47780     deferHeight: true,
47781     initialized : false,
47782     activated : false,
47783     
47784     onFocus : Roo.emptyFn,
47785     iframePad:3,
47786     hideMode:'offsets',
47787     
47788     actionMode : 'container', // defaults to hiding it...
47789     
47790     defaultAutoCreate : { // modified by initCompnoent..
47791         tag: "textarea",
47792         style:"width:500px;height:300px;",
47793         autocomplete: "new-password"
47794     },
47795
47796     // private
47797     initComponent : function(){
47798         this.addEvents({
47799             /**
47800              * @event initialize
47801              * Fires when the editor is fully initialized (including the iframe)
47802              * @param {HtmlEditor} this
47803              */
47804             initialize: true,
47805             /**
47806              * @event activate
47807              * Fires when the editor is first receives the focus. Any insertion must wait
47808              * until after this event.
47809              * @param {HtmlEditor} this
47810              */
47811             activate: true,
47812              /**
47813              * @event beforesync
47814              * Fires before the textarea is updated with content from the editor iframe. Return false
47815              * to cancel the sync.
47816              * @param {HtmlEditor} this
47817              * @param {String} html
47818              */
47819             beforesync: true,
47820              /**
47821              * @event beforepush
47822              * Fires before the iframe editor is updated with content from the textarea. Return false
47823              * to cancel the push.
47824              * @param {HtmlEditor} this
47825              * @param {String} html
47826              */
47827             beforepush: true,
47828              /**
47829              * @event sync
47830              * Fires when the textarea is updated with content from the editor iframe.
47831              * @param {HtmlEditor} this
47832              * @param {String} html
47833              */
47834             sync: true,
47835              /**
47836              * @event push
47837              * Fires when the iframe editor is updated with content from the textarea.
47838              * @param {HtmlEditor} this
47839              * @param {String} html
47840              */
47841             push: true,
47842              /**
47843              * @event editmodechange
47844              * Fires when the editor switches edit modes
47845              * @param {HtmlEditor} this
47846              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47847              */
47848             editmodechange: true,
47849             /**
47850              * @event editorevent
47851              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47852              * @param {HtmlEditor} this
47853              */
47854             editorevent: true,
47855             /**
47856              * @event firstfocus
47857              * Fires when on first focus - needed by toolbars..
47858              * @param {HtmlEditor} this
47859              */
47860             firstfocus: true,
47861             /**
47862              * @event autosave
47863              * Auto save the htmlEditor value as a file into Events
47864              * @param {HtmlEditor} this
47865              */
47866             autosave: true,
47867             /**
47868              * @event savedpreview
47869              * preview the saved version of htmlEditor
47870              * @param {HtmlEditor} this
47871              */
47872             savedpreview: true,
47873             
47874             /**
47875             * @event stylesheetsclick
47876             * Fires when press the Sytlesheets button
47877             * @param {Roo.HtmlEditorCore} this
47878             */
47879             stylesheetsclick: true,
47880             /**
47881             * @event paste
47882             * Fires when press user pastes into the editor
47883             * @param {Roo.HtmlEditorCore} this
47884             */
47885             paste: true 
47886         });
47887         this.defaultAutoCreate =  {
47888             tag: "textarea",
47889             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47890             autocomplete: "new-password"
47891         };
47892     },
47893
47894     /**
47895      * Protected method that will not generally be called directly. It
47896      * is called when the editor creates its toolbar. Override this method if you need to
47897      * add custom toolbar buttons.
47898      * @param {HtmlEditor} editor
47899      */
47900     createToolbar : function(editor){
47901         Roo.log("create toolbars");
47902         if (!editor.toolbars || !editor.toolbars.length) {
47903             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47904         }
47905         
47906         for (var i =0 ; i < editor.toolbars.length;i++) {
47907             editor.toolbars[i] = Roo.factory(
47908                     typeof(editor.toolbars[i]) == 'string' ?
47909                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47910                 Roo.form.HtmlEditor);
47911             editor.toolbars[i].init(editor);
47912         }
47913          
47914         
47915     },
47916
47917      
47918     // private
47919     onRender : function(ct, position)
47920     {
47921         var _t = this;
47922         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47923         
47924         this.wrap = this.el.wrap({
47925             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47926         });
47927         
47928         this.editorcore.onRender(ct, position);
47929          
47930         if (this.resizable) {
47931             this.resizeEl = new Roo.Resizable(this.wrap, {
47932                 pinned : true,
47933                 wrap: true,
47934                 dynamic : true,
47935                 minHeight : this.height,
47936                 height: this.height,
47937                 handles : this.resizable,
47938                 width: this.width,
47939                 listeners : {
47940                     resize : function(r, w, h) {
47941                         _t.onResize(w,h); // -something
47942                     }
47943                 }
47944             });
47945             
47946         }
47947         this.createToolbar(this);
47948        
47949         
47950         if(!this.width){
47951             this.setSize(this.wrap.getSize());
47952         }
47953         if (this.resizeEl) {
47954             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47955             // should trigger onReize..
47956         }
47957         
47958         this.keyNav = new Roo.KeyNav(this.el, {
47959             
47960             "tab" : function(e){
47961                 e.preventDefault();
47962                 
47963                 var value = this.getValue();
47964                 
47965                 var start = this.el.dom.selectionStart;
47966                 var end = this.el.dom.selectionEnd;
47967                 
47968                 if(!e.shiftKey){
47969                     
47970                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47971                     this.el.dom.setSelectionRange(end + 1, end + 1);
47972                     return;
47973                 }
47974                 
47975                 var f = value.substring(0, start).split("\t");
47976                 
47977                 if(f.pop().length != 0){
47978                     return;
47979                 }
47980                 
47981                 this.setValue(f.join("\t") + value.substring(end));
47982                 this.el.dom.setSelectionRange(start - 1, start - 1);
47983                 
47984             },
47985             
47986             "home" : function(e){
47987                 e.preventDefault();
47988                 
47989                 var curr = this.el.dom.selectionStart;
47990                 var lines = this.getValue().split("\n");
47991                 
47992                 if(!lines.length){
47993                     return;
47994                 }
47995                 
47996                 if(e.ctrlKey){
47997                     this.el.dom.setSelectionRange(0, 0);
47998                     return;
47999                 }
48000                 
48001                 var pos = 0;
48002                 
48003                 for (var i = 0; i < lines.length;i++) {
48004                     pos += lines[i].length;
48005                     
48006                     if(i != 0){
48007                         pos += 1;
48008                     }
48009                     
48010                     if(pos < curr){
48011                         continue;
48012                     }
48013                     
48014                     pos -= lines[i].length;
48015                     
48016                     break;
48017                 }
48018                 
48019                 if(!e.shiftKey){
48020                     this.el.dom.setSelectionRange(pos, pos);
48021                     return;
48022                 }
48023                 
48024                 this.el.dom.selectionStart = pos;
48025                 this.el.dom.selectionEnd = curr;
48026             },
48027             
48028             "end" : function(e){
48029                 e.preventDefault();
48030                 
48031                 var curr = this.el.dom.selectionStart;
48032                 var lines = this.getValue().split("\n");
48033                 
48034                 if(!lines.length){
48035                     return;
48036                 }
48037                 
48038                 if(e.ctrlKey){
48039                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48040                     return;
48041                 }
48042                 
48043                 var pos = 0;
48044                 
48045                 for (var i = 0; i < lines.length;i++) {
48046                     
48047                     pos += lines[i].length;
48048                     
48049                     if(i != 0){
48050                         pos += 1;
48051                     }
48052                     
48053                     if(pos < curr){
48054                         continue;
48055                     }
48056                     
48057                     break;
48058                 }
48059                 
48060                 if(!e.shiftKey){
48061                     this.el.dom.setSelectionRange(pos, pos);
48062                     return;
48063                 }
48064                 
48065                 this.el.dom.selectionStart = curr;
48066                 this.el.dom.selectionEnd = pos;
48067             },
48068
48069             scope : this,
48070
48071             doRelay : function(foo, bar, hname){
48072                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48073             },
48074
48075             forceKeyDown: true
48076         });
48077         
48078 //        if(this.autosave && this.w){
48079 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48080 //        }
48081     },
48082
48083     // private
48084     onResize : function(w, h)
48085     {
48086         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48087         var ew = false;
48088         var eh = false;
48089         
48090         if(this.el ){
48091             if(typeof w == 'number'){
48092                 var aw = w - this.wrap.getFrameWidth('lr');
48093                 this.el.setWidth(this.adjustWidth('textarea', aw));
48094                 ew = aw;
48095             }
48096             if(typeof h == 'number'){
48097                 var tbh = 0;
48098                 for (var i =0; i < this.toolbars.length;i++) {
48099                     // fixme - ask toolbars for heights?
48100                     tbh += this.toolbars[i].tb.el.getHeight();
48101                     if (this.toolbars[i].footer) {
48102                         tbh += this.toolbars[i].footer.el.getHeight();
48103                     }
48104                 }
48105                 
48106                 
48107                 
48108                 
48109                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48110                 ah -= 5; // knock a few pixes off for look..
48111 //                Roo.log(ah);
48112                 this.el.setHeight(this.adjustWidth('textarea', ah));
48113                 var eh = ah;
48114             }
48115         }
48116         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48117         this.editorcore.onResize(ew,eh);
48118         
48119     },
48120
48121     /**
48122      * Toggles the editor between standard and source edit mode.
48123      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48124      */
48125     toggleSourceEdit : function(sourceEditMode)
48126     {
48127         this.editorcore.toggleSourceEdit(sourceEditMode);
48128         
48129         if(this.editorcore.sourceEditMode){
48130             Roo.log('editor - showing textarea');
48131             
48132 //            Roo.log('in');
48133 //            Roo.log(this.syncValue());
48134             this.editorcore.syncValue();
48135             this.el.removeClass('x-hidden');
48136             this.el.dom.removeAttribute('tabIndex');
48137             this.el.focus();
48138             this.el.dom.scrollTop = 0;
48139             
48140             
48141             for (var i = 0; i < this.toolbars.length; i++) {
48142                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48143                     this.toolbars[i].tb.hide();
48144                     this.toolbars[i].footer.hide();
48145                 }
48146             }
48147             
48148         }else{
48149             Roo.log('editor - hiding textarea');
48150 //            Roo.log('out')
48151 //            Roo.log(this.pushValue()); 
48152             this.editorcore.pushValue();
48153             
48154             this.el.addClass('x-hidden');
48155             this.el.dom.setAttribute('tabIndex', -1);
48156             
48157             for (var i = 0; i < this.toolbars.length; i++) {
48158                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48159                     this.toolbars[i].tb.show();
48160                     this.toolbars[i].footer.show();
48161                 }
48162             }
48163             
48164             //this.deferFocus();
48165         }
48166         
48167         this.setSize(this.wrap.getSize());
48168         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48169         
48170         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48171     },
48172  
48173     // private (for BoxComponent)
48174     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48175
48176     // private (for BoxComponent)
48177     getResizeEl : function(){
48178         return this.wrap;
48179     },
48180
48181     // private (for BoxComponent)
48182     getPositionEl : function(){
48183         return this.wrap;
48184     },
48185
48186     // private
48187     initEvents : function(){
48188         this.originalValue = this.getValue();
48189     },
48190
48191     /**
48192      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48193      * @method
48194      */
48195     markInvalid : Roo.emptyFn,
48196     /**
48197      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48198      * @method
48199      */
48200     clearInvalid : Roo.emptyFn,
48201
48202     setValue : function(v){
48203         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48204         this.editorcore.pushValue();
48205     },
48206
48207      
48208     // private
48209     deferFocus : function(){
48210         this.focus.defer(10, this);
48211     },
48212
48213     // doc'ed in Field
48214     focus : function(){
48215         this.editorcore.focus();
48216         
48217     },
48218       
48219
48220     // private
48221     onDestroy : function(){
48222         
48223         
48224         
48225         if(this.rendered){
48226             
48227             for (var i =0; i < this.toolbars.length;i++) {
48228                 // fixme - ask toolbars for heights?
48229                 this.toolbars[i].onDestroy();
48230             }
48231             
48232             this.wrap.dom.innerHTML = '';
48233             this.wrap.remove();
48234         }
48235     },
48236
48237     // private
48238     onFirstFocus : function(){
48239         //Roo.log("onFirstFocus");
48240         this.editorcore.onFirstFocus();
48241          for (var i =0; i < this.toolbars.length;i++) {
48242             this.toolbars[i].onFirstFocus();
48243         }
48244         
48245     },
48246     
48247     // private
48248     syncValue : function()
48249     {
48250         this.editorcore.syncValue();
48251     },
48252     
48253     pushValue : function()
48254     {
48255         this.editorcore.pushValue();
48256     },
48257     
48258     setStylesheets : function(stylesheets)
48259     {
48260         this.editorcore.setStylesheets(stylesheets);
48261     },
48262     
48263     removeStylesheets : function()
48264     {
48265         this.editorcore.removeStylesheets();
48266     }
48267      
48268     
48269     // hide stuff that is not compatible
48270     /**
48271      * @event blur
48272      * @hide
48273      */
48274     /**
48275      * @event change
48276      * @hide
48277      */
48278     /**
48279      * @event focus
48280      * @hide
48281      */
48282     /**
48283      * @event specialkey
48284      * @hide
48285      */
48286     /**
48287      * @cfg {String} fieldClass @hide
48288      */
48289     /**
48290      * @cfg {String} focusClass @hide
48291      */
48292     /**
48293      * @cfg {String} autoCreate @hide
48294      */
48295     /**
48296      * @cfg {String} inputType @hide
48297      */
48298     /**
48299      * @cfg {String} invalidClass @hide
48300      */
48301     /**
48302      * @cfg {String} invalidText @hide
48303      */
48304     /**
48305      * @cfg {String} msgFx @hide
48306      */
48307     /**
48308      * @cfg {String} validateOnBlur @hide
48309      */
48310 });
48311  
48312     // <script type="text/javascript">
48313 /*
48314  * Based on
48315  * Ext JS Library 1.1.1
48316  * Copyright(c) 2006-2007, Ext JS, LLC.
48317  *  
48318  
48319  */
48320
48321 /**
48322  * @class Roo.form.HtmlEditorToolbar1
48323  * Basic Toolbar
48324  * 
48325  * Usage:
48326  *
48327  new Roo.form.HtmlEditor({
48328     ....
48329     toolbars : [
48330         new Roo.form.HtmlEditorToolbar1({
48331             disable : { fonts: 1 , format: 1, ..., ... , ...],
48332             btns : [ .... ]
48333         })
48334     }
48335      
48336  * 
48337  * @cfg {Object} disable List of elements to disable..
48338  * @cfg {Array} btns List of additional buttons.
48339  * 
48340  * 
48341  * NEEDS Extra CSS? 
48342  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48343  */
48344  
48345 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48346 {
48347     
48348     Roo.apply(this, config);
48349     
48350     // default disabled, based on 'good practice'..
48351     this.disable = this.disable || {};
48352     Roo.applyIf(this.disable, {
48353         fontSize : true,
48354         colors : true,
48355         specialElements : true
48356     });
48357     
48358     
48359     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48360     // dont call parent... till later.
48361 }
48362
48363 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48364     
48365     tb: false,
48366     
48367     rendered: false,
48368     
48369     editor : false,
48370     editorcore : false,
48371     /**
48372      * @cfg {Object} disable  List of toolbar elements to disable
48373          
48374      */
48375     disable : false,
48376     
48377     
48378      /**
48379      * @cfg {String} createLinkText The default text for the create link prompt
48380      */
48381     createLinkText : 'Please enter the URL for the link:',
48382     /**
48383      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48384      */
48385     defaultLinkValue : 'http:/'+'/',
48386    
48387     
48388       /**
48389      * @cfg {Array} fontFamilies An array of available font families
48390      */
48391     fontFamilies : [
48392         'Arial',
48393         'Courier New',
48394         'Tahoma',
48395         'Times New Roman',
48396         'Verdana'
48397     ],
48398     
48399     specialChars : [
48400            "&#169;",
48401           "&#174;",     
48402           "&#8482;",    
48403           "&#163;" ,    
48404          // "&#8212;",    
48405           "&#8230;",    
48406           "&#247;" ,    
48407         //  "&#225;" ,     ?? a acute?
48408            "&#8364;"    , //Euro
48409        //   "&#8220;"    ,
48410         //  "&#8221;"    ,
48411         //  "&#8226;"    ,
48412           "&#176;"  //   , // degrees
48413
48414          // "&#233;"     , // e ecute
48415          // "&#250;"     , // u ecute?
48416     ],
48417     
48418     specialElements : [
48419         {
48420             text: "Insert Table",
48421             xtype: 'MenuItem',
48422             xns : Roo.Menu,
48423             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48424                 
48425         },
48426         {    
48427             text: "Insert Image",
48428             xtype: 'MenuItem',
48429             xns : Roo.Menu,
48430             ihtml : '<img src="about:blank"/>'
48431             
48432         }
48433         
48434          
48435     ],
48436     
48437     
48438     inputElements : [ 
48439             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48440             "input:submit", "input:button", "select", "textarea", "label" ],
48441     formats : [
48442         ["p"] ,  
48443         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48444         ["pre"],[ "code"], 
48445         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48446         ['div'],['span'],
48447         ['sup'],['sub']
48448     ],
48449     
48450     cleanStyles : [
48451         "font-size"
48452     ],
48453      /**
48454      * @cfg {String} defaultFont default font to use.
48455      */
48456     defaultFont: 'tahoma',
48457    
48458     fontSelect : false,
48459     
48460     
48461     formatCombo : false,
48462     
48463     init : function(editor)
48464     {
48465         this.editor = editor;
48466         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48467         var editorcore = this.editorcore;
48468         
48469         var _t = this;
48470         
48471         var fid = editorcore.frameId;
48472         var etb = this;
48473         function btn(id, toggle, handler){
48474             var xid = fid + '-'+ id ;
48475             return {
48476                 id : xid,
48477                 cmd : id,
48478                 cls : 'x-btn-icon x-edit-'+id,
48479                 enableToggle:toggle !== false,
48480                 scope: _t, // was editor...
48481                 handler:handler||_t.relayBtnCmd,
48482                 clickEvent:'mousedown',
48483                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48484                 tabIndex:-1
48485             };
48486         }
48487         
48488         
48489         
48490         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48491         this.tb = tb;
48492          // stop form submits
48493         tb.el.on('click', function(e){
48494             e.preventDefault(); // what does this do?
48495         });
48496
48497         if(!this.disable.font) { // && !Roo.isSafari){
48498             /* why no safari for fonts 
48499             editor.fontSelect = tb.el.createChild({
48500                 tag:'select',
48501                 tabIndex: -1,
48502                 cls:'x-font-select',
48503                 html: this.createFontOptions()
48504             });
48505             
48506             editor.fontSelect.on('change', function(){
48507                 var font = editor.fontSelect.dom.value;
48508                 editor.relayCmd('fontname', font);
48509                 editor.deferFocus();
48510             }, editor);
48511             
48512             tb.add(
48513                 editor.fontSelect.dom,
48514                 '-'
48515             );
48516             */
48517             
48518         };
48519         if(!this.disable.formats){
48520             this.formatCombo = new Roo.form.ComboBox({
48521                 store: new Roo.data.SimpleStore({
48522                     id : 'tag',
48523                     fields: ['tag'],
48524                     data : this.formats // from states.js
48525                 }),
48526                 blockFocus : true,
48527                 name : '',
48528                 //autoCreate : {tag: "div",  size: "20"},
48529                 displayField:'tag',
48530                 typeAhead: false,
48531                 mode: 'local',
48532                 editable : false,
48533                 triggerAction: 'all',
48534                 emptyText:'Add tag',
48535                 selectOnFocus:true,
48536                 width:135,
48537                 listeners : {
48538                     'select': function(c, r, i) {
48539                         editorcore.insertTag(r.get('tag'));
48540                         editor.focus();
48541                     }
48542                 }
48543
48544             });
48545             tb.addField(this.formatCombo);
48546             
48547         }
48548         
48549         if(!this.disable.format){
48550             tb.add(
48551                 btn('bold'),
48552                 btn('italic'),
48553                 btn('underline'),
48554                 btn('strikethrough')
48555             );
48556         };
48557         if(!this.disable.fontSize){
48558             tb.add(
48559                 '-',
48560                 
48561                 
48562                 btn('increasefontsize', false, editorcore.adjustFont),
48563                 btn('decreasefontsize', false, editorcore.adjustFont)
48564             );
48565         };
48566         
48567         
48568         if(!this.disable.colors){
48569             tb.add(
48570                 '-', {
48571                     id:editorcore.frameId +'-forecolor',
48572                     cls:'x-btn-icon x-edit-forecolor',
48573                     clickEvent:'mousedown',
48574                     tooltip: this.buttonTips['forecolor'] || undefined,
48575                     tabIndex:-1,
48576                     menu : new Roo.menu.ColorMenu({
48577                         allowReselect: true,
48578                         focus: Roo.emptyFn,
48579                         value:'000000',
48580                         plain:true,
48581                         selectHandler: function(cp, color){
48582                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48583                             editor.deferFocus();
48584                         },
48585                         scope: editorcore,
48586                         clickEvent:'mousedown'
48587                     })
48588                 }, {
48589                     id:editorcore.frameId +'backcolor',
48590                     cls:'x-btn-icon x-edit-backcolor',
48591                     clickEvent:'mousedown',
48592                     tooltip: this.buttonTips['backcolor'] || undefined,
48593                     tabIndex:-1,
48594                     menu : new Roo.menu.ColorMenu({
48595                         focus: Roo.emptyFn,
48596                         value:'FFFFFF',
48597                         plain:true,
48598                         allowReselect: true,
48599                         selectHandler: function(cp, color){
48600                             if(Roo.isGecko){
48601                                 editorcore.execCmd('useCSS', false);
48602                                 editorcore.execCmd('hilitecolor', color);
48603                                 editorcore.execCmd('useCSS', true);
48604                                 editor.deferFocus();
48605                             }else{
48606                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48607                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48608                                 editor.deferFocus();
48609                             }
48610                         },
48611                         scope:editorcore,
48612                         clickEvent:'mousedown'
48613                     })
48614                 }
48615             );
48616         };
48617         // now add all the items...
48618         
48619
48620         if(!this.disable.alignments){
48621             tb.add(
48622                 '-',
48623                 btn('justifyleft'),
48624                 btn('justifycenter'),
48625                 btn('justifyright')
48626             );
48627         };
48628
48629         //if(!Roo.isSafari){
48630             if(!this.disable.links){
48631                 tb.add(
48632                     '-',
48633                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48634                 );
48635             };
48636
48637             if(!this.disable.lists){
48638                 tb.add(
48639                     '-',
48640                     btn('insertorderedlist'),
48641                     btn('insertunorderedlist')
48642                 );
48643             }
48644             if(!this.disable.sourceEdit){
48645                 tb.add(
48646                     '-',
48647                     btn('sourceedit', true, function(btn){
48648                         this.toggleSourceEdit(btn.pressed);
48649                     })
48650                 );
48651             }
48652         //}
48653         
48654         var smenu = { };
48655         // special menu.. - needs to be tidied up..
48656         if (!this.disable.special) {
48657             smenu = {
48658                 text: "&#169;",
48659                 cls: 'x-edit-none',
48660                 
48661                 menu : {
48662                     items : []
48663                 }
48664             };
48665             for (var i =0; i < this.specialChars.length; i++) {
48666                 smenu.menu.items.push({
48667                     
48668                     html: this.specialChars[i],
48669                     handler: function(a,b) {
48670                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48671                         //editor.insertAtCursor(a.html);
48672                         
48673                     },
48674                     tabIndex:-1
48675                 });
48676             }
48677             
48678             
48679             tb.add(smenu);
48680             
48681             
48682         }
48683         
48684         var cmenu = { };
48685         if (!this.disable.cleanStyles) {
48686             cmenu = {
48687                 cls: 'x-btn-icon x-btn-clear',
48688                 
48689                 menu : {
48690                     items : []
48691                 }
48692             };
48693             for (var i =0; i < this.cleanStyles.length; i++) {
48694                 cmenu.menu.items.push({
48695                     actiontype : this.cleanStyles[i],
48696                     html: 'Remove ' + this.cleanStyles[i],
48697                     handler: function(a,b) {
48698 //                        Roo.log(a);
48699 //                        Roo.log(b);
48700                         var c = Roo.get(editorcore.doc.body);
48701                         c.select('[style]').each(function(s) {
48702                             s.dom.style.removeProperty(a.actiontype);
48703                         });
48704                         editorcore.syncValue();
48705                     },
48706                     tabIndex:-1
48707                 });
48708             }
48709             cmenu.menu.items.push({
48710                 actiontype : 'tablewidths',
48711                 html: 'Remove Table Widths',
48712                 handler: function(a,b) {
48713                     editorcore.cleanTableWidths();
48714                     editorcore.syncValue();
48715                 },
48716                 tabIndex:-1
48717             });
48718             cmenu.menu.items.push({
48719                 actiontype : 'word',
48720                 html: 'Remove MS Word Formating',
48721                 handler: function(a,b) {
48722                     editorcore.cleanWord();
48723                     editorcore.syncValue();
48724                 },
48725                 tabIndex:-1
48726             });
48727             
48728             cmenu.menu.items.push({
48729                 actiontype : 'all',
48730                 html: 'Remove All Styles',
48731                 handler: function(a,b) {
48732                     
48733                     var c = Roo.get(editorcore.doc.body);
48734                     c.select('[style]').each(function(s) {
48735                         s.dom.removeAttribute('style');
48736                     });
48737                     editorcore.syncValue();
48738                 },
48739                 tabIndex:-1
48740             });
48741             
48742             cmenu.menu.items.push({
48743                 actiontype : 'all',
48744                 html: 'Remove All CSS Classes',
48745                 handler: function(a,b) {
48746                     
48747                     var c = Roo.get(editorcore.doc.body);
48748                     c.select('[class]').each(function(s) {
48749                         s.dom.removeAttribute('class');
48750                     });
48751                     editorcore.cleanWord();
48752                     editorcore.syncValue();
48753                 },
48754                 tabIndex:-1
48755             });
48756             
48757              cmenu.menu.items.push({
48758                 actiontype : 'tidy',
48759                 html: 'Tidy HTML Source',
48760                 handler: function(a,b) {
48761                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48762                     editorcore.syncValue();
48763                 },
48764                 tabIndex:-1
48765             });
48766             
48767             
48768             tb.add(cmenu);
48769         }
48770          
48771         if (!this.disable.specialElements) {
48772             var semenu = {
48773                 text: "Other;",
48774                 cls: 'x-edit-none',
48775                 menu : {
48776                     items : []
48777                 }
48778             };
48779             for (var i =0; i < this.specialElements.length; i++) {
48780                 semenu.menu.items.push(
48781                     Roo.apply({ 
48782                         handler: function(a,b) {
48783                             editor.insertAtCursor(this.ihtml);
48784                         }
48785                     }, this.specialElements[i])
48786                 );
48787                     
48788             }
48789             
48790             tb.add(semenu);
48791             
48792             
48793         }
48794          
48795         
48796         if (this.btns) {
48797             for(var i =0; i< this.btns.length;i++) {
48798                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
48799                 b.cls =  'x-edit-none';
48800                 
48801                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48802                     b.cls += ' x-init-enable';
48803                 }
48804                 
48805                 b.scope = editorcore;
48806                 tb.add(b);
48807             }
48808         
48809         }
48810         
48811         
48812         
48813         // disable everything...
48814         
48815         this.tb.items.each(function(item){
48816             
48817            if(
48818                 item.id != editorcore.frameId+ '-sourceedit' && 
48819                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48820             ){
48821                 
48822                 item.disable();
48823             }
48824         });
48825         this.rendered = true;
48826         
48827         // the all the btns;
48828         editor.on('editorevent', this.updateToolbar, this);
48829         // other toolbars need to implement this..
48830         //editor.on('editmodechange', this.updateToolbar, this);
48831     },
48832     
48833     
48834     relayBtnCmd : function(btn) {
48835         this.editorcore.relayCmd(btn.cmd);
48836     },
48837     // private used internally
48838     createLink : function(){
48839         Roo.log("create link?");
48840         var url = prompt(this.createLinkText, this.defaultLinkValue);
48841         if(url && url != 'http:/'+'/'){
48842             this.editorcore.relayCmd('createlink', url);
48843         }
48844     },
48845
48846     
48847     /**
48848      * Protected method that will not generally be called directly. It triggers
48849      * a toolbar update by reading the markup state of the current selection in the editor.
48850      */
48851     updateToolbar: function(){
48852
48853         if(!this.editorcore.activated){
48854             this.editor.onFirstFocus();
48855             return;
48856         }
48857
48858         var btns = this.tb.items.map, 
48859             doc = this.editorcore.doc,
48860             frameId = this.editorcore.frameId;
48861
48862         if(!this.disable.font && !Roo.isSafari){
48863             /*
48864             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48865             if(name != this.fontSelect.dom.value){
48866                 this.fontSelect.dom.value = name;
48867             }
48868             */
48869         }
48870         if(!this.disable.format){
48871             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48872             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48873             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48874             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48875         }
48876         if(!this.disable.alignments){
48877             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48878             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48879             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48880         }
48881         if(!Roo.isSafari && !this.disable.lists){
48882             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48883             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48884         }
48885         
48886         var ans = this.editorcore.getAllAncestors();
48887         if (this.formatCombo) {
48888             
48889             
48890             var store = this.formatCombo.store;
48891             this.formatCombo.setValue("");
48892             for (var i =0; i < ans.length;i++) {
48893                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48894                     // select it..
48895                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48896                     break;
48897                 }
48898             }
48899         }
48900         
48901         
48902         
48903         // hides menus... - so this cant be on a menu...
48904         Roo.menu.MenuMgr.hideAll();
48905
48906         //this.editorsyncValue();
48907     },
48908    
48909     
48910     createFontOptions : function(){
48911         var buf = [], fs = this.fontFamilies, ff, lc;
48912         
48913         
48914         
48915         for(var i = 0, len = fs.length; i< len; i++){
48916             ff = fs[i];
48917             lc = ff.toLowerCase();
48918             buf.push(
48919                 '<option value="',lc,'" style="font-family:',ff,';"',
48920                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48921                     ff,
48922                 '</option>'
48923             );
48924         }
48925         return buf.join('');
48926     },
48927     
48928     toggleSourceEdit : function(sourceEditMode){
48929         
48930         Roo.log("toolbar toogle");
48931         if(sourceEditMode === undefined){
48932             sourceEditMode = !this.sourceEditMode;
48933         }
48934         this.sourceEditMode = sourceEditMode === true;
48935         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48936         // just toggle the button?
48937         if(btn.pressed !== this.sourceEditMode){
48938             btn.toggle(this.sourceEditMode);
48939             return;
48940         }
48941         
48942         if(sourceEditMode){
48943             Roo.log("disabling buttons");
48944             this.tb.items.each(function(item){
48945                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48946                     item.disable();
48947                 }
48948             });
48949           
48950         }else{
48951             Roo.log("enabling buttons");
48952             if(this.editorcore.initialized){
48953                 this.tb.items.each(function(item){
48954                     item.enable();
48955                 });
48956             }
48957             
48958         }
48959         Roo.log("calling toggole on editor");
48960         // tell the editor that it's been pressed..
48961         this.editor.toggleSourceEdit(sourceEditMode);
48962        
48963     },
48964      /**
48965      * Object collection of toolbar tooltips for the buttons in the editor. The key
48966      * is the command id associated with that button and the value is a valid QuickTips object.
48967      * For example:
48968 <pre><code>
48969 {
48970     bold : {
48971         title: 'Bold (Ctrl+B)',
48972         text: 'Make the selected text bold.',
48973         cls: 'x-html-editor-tip'
48974     },
48975     italic : {
48976         title: 'Italic (Ctrl+I)',
48977         text: 'Make the selected text italic.',
48978         cls: 'x-html-editor-tip'
48979     },
48980     ...
48981 </code></pre>
48982     * @type Object
48983      */
48984     buttonTips : {
48985         bold : {
48986             title: 'Bold (Ctrl+B)',
48987             text: 'Make the selected text bold.',
48988             cls: 'x-html-editor-tip'
48989         },
48990         italic : {
48991             title: 'Italic (Ctrl+I)',
48992             text: 'Make the selected text italic.',
48993             cls: 'x-html-editor-tip'
48994         },
48995         underline : {
48996             title: 'Underline (Ctrl+U)',
48997             text: 'Underline the selected text.',
48998             cls: 'x-html-editor-tip'
48999         },
49000         strikethrough : {
49001             title: 'Strikethrough',
49002             text: 'Strikethrough the selected text.',
49003             cls: 'x-html-editor-tip'
49004         },
49005         increasefontsize : {
49006             title: 'Grow Text',
49007             text: 'Increase the font size.',
49008             cls: 'x-html-editor-tip'
49009         },
49010         decreasefontsize : {
49011             title: 'Shrink Text',
49012             text: 'Decrease the font size.',
49013             cls: 'x-html-editor-tip'
49014         },
49015         backcolor : {
49016             title: 'Text Highlight Color',
49017             text: 'Change the background color of the selected text.',
49018             cls: 'x-html-editor-tip'
49019         },
49020         forecolor : {
49021             title: 'Font Color',
49022             text: 'Change the color of the selected text.',
49023             cls: 'x-html-editor-tip'
49024         },
49025         justifyleft : {
49026             title: 'Align Text Left',
49027             text: 'Align text to the left.',
49028             cls: 'x-html-editor-tip'
49029         },
49030         justifycenter : {
49031             title: 'Center Text',
49032             text: 'Center text in the editor.',
49033             cls: 'x-html-editor-tip'
49034         },
49035         justifyright : {
49036             title: 'Align Text Right',
49037             text: 'Align text to the right.',
49038             cls: 'x-html-editor-tip'
49039         },
49040         insertunorderedlist : {
49041             title: 'Bullet List',
49042             text: 'Start a bulleted list.',
49043             cls: 'x-html-editor-tip'
49044         },
49045         insertorderedlist : {
49046             title: 'Numbered List',
49047             text: 'Start a numbered list.',
49048             cls: 'x-html-editor-tip'
49049         },
49050         createlink : {
49051             title: 'Hyperlink',
49052             text: 'Make the selected text a hyperlink.',
49053             cls: 'x-html-editor-tip'
49054         },
49055         sourceedit : {
49056             title: 'Source Edit',
49057             text: 'Switch to source editing mode.',
49058             cls: 'x-html-editor-tip'
49059         }
49060     },
49061     // private
49062     onDestroy : function(){
49063         if(this.rendered){
49064             
49065             this.tb.items.each(function(item){
49066                 if(item.menu){
49067                     item.menu.removeAll();
49068                     if(item.menu.el){
49069                         item.menu.el.destroy();
49070                     }
49071                 }
49072                 item.destroy();
49073             });
49074              
49075         }
49076     },
49077     onFirstFocus: function() {
49078         this.tb.items.each(function(item){
49079            item.enable();
49080         });
49081     }
49082 });
49083
49084
49085
49086
49087 // <script type="text/javascript">
49088 /*
49089  * Based on
49090  * Ext JS Library 1.1.1
49091  * Copyright(c) 2006-2007, Ext JS, LLC.
49092  *  
49093  
49094  */
49095
49096  
49097 /**
49098  * @class Roo.form.HtmlEditor.ToolbarContext
49099  * Context Toolbar
49100  * 
49101  * Usage:
49102  *
49103  new Roo.form.HtmlEditor({
49104     ....
49105     toolbars : [
49106         { xtype: 'ToolbarStandard', styles : {} }
49107         { xtype: 'ToolbarContext', disable : {} }
49108     ]
49109 })
49110
49111      
49112  * 
49113  * @config : {Object} disable List of elements to disable.. (not done yet.)
49114  * @config : {Object} styles  Map of styles available.
49115  * 
49116  */
49117
49118 Roo.form.HtmlEditor.ToolbarContext = function(config)
49119 {
49120     
49121     Roo.apply(this, config);
49122     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49123     // dont call parent... till later.
49124     this.styles = this.styles || {};
49125 }
49126
49127  
49128
49129 Roo.form.HtmlEditor.ToolbarContext.types = {
49130     'IMG' : [
49131         {
49132             name : 'width',
49133             title: "Width",
49134             width: 40
49135         },
49136         {
49137             name : 'height',
49138             title: "Height",
49139             width: 40
49140         },
49141         {
49142             name : 'align',
49143             title: "Align",
49144             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49145             width : 80
49146             
49147         },
49148         {
49149             name : 'border',
49150             title: "Border",
49151             width: 40
49152         },
49153         {
49154             name : 'alt',
49155             title: "Alt",
49156             width: 120
49157         },
49158         {
49159             name : 'src',
49160             title: "Src",
49161             width: 220
49162         }
49163         
49164     ],
49165     
49166     'FIGURE' : [
49167         {
49168             name : 'align',
49169             title: "Align",
49170             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49171             width : 80  
49172         }
49173     ],
49174     'A' : [
49175         {
49176             name : 'name',
49177             title: "Name",
49178             width: 50
49179         },
49180         {
49181             name : 'target',
49182             title: "Target",
49183             width: 120
49184         },
49185         {
49186             name : 'href',
49187             title: "Href",
49188             width: 220
49189         } // border?
49190         
49191     ],
49192     
49193     'INPUT' : [
49194         {
49195             name : 'name',
49196             title: "name",
49197             width: 120
49198         },
49199         {
49200             name : 'value',
49201             title: "Value",
49202             width: 120
49203         },
49204         {
49205             name : 'width',
49206             title: "Width",
49207             width: 40
49208         }
49209     ],
49210     'LABEL' : [
49211          {
49212             name : 'for',
49213             title: "For",
49214             width: 120
49215         }
49216     ],
49217     'TEXTAREA' : [
49218         {
49219             name : 'name',
49220             title: "name",
49221             width: 120
49222         },
49223         {
49224             name : 'rows',
49225             title: "Rows",
49226             width: 20
49227         },
49228         {
49229             name : 'cols',
49230             title: "Cols",
49231             width: 20
49232         }
49233     ],
49234     'SELECT' : [
49235         {
49236             name : 'name',
49237             title: "name",
49238             width: 120
49239         },
49240         {
49241             name : 'selectoptions',
49242             title: "Options",
49243             width: 200
49244         }
49245     ],
49246     
49247     // should we really allow this??
49248     // should this just be 
49249     'BODY' : [
49250         
49251         {
49252             name : 'title',
49253             title: "Title",
49254             width: 200,
49255             disabled : true
49256         }
49257     ],
49258  
49259     '*' : [
49260         // empty.
49261     ]
49262
49263 };
49264
49265 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49266 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49267
49268 Roo.form.HtmlEditor.ToolbarContext.options = {
49269         'font-family'  : [ 
49270                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49271                 [ 'Courier New', 'Courier New'],
49272                 [ 'Tahoma', 'Tahoma'],
49273                 [ 'Times New Roman,serif', 'Times'],
49274                 [ 'Verdana','Verdana' ]
49275         ]
49276 };
49277
49278 // fixme - these need to be configurable..
49279  
49280
49281 //Roo.form.HtmlEditor.ToolbarContext.types
49282
49283
49284 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49285     
49286     tb: false,
49287     
49288     rendered: false,
49289     
49290     editor : false,
49291     editorcore : false,
49292     /**
49293      * @cfg {Object} disable  List of toolbar elements to disable
49294          
49295      */
49296     disable : false,
49297     /**
49298      * @cfg {Object} styles List of styles 
49299      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49300      *
49301      * These must be defined in the page, so they get rendered correctly..
49302      * .headline { }
49303      * TD.underline { }
49304      * 
49305      */
49306     styles : false,
49307     
49308     options: false,
49309     
49310     toolbars : false,
49311     
49312     init : function(editor)
49313     {
49314         this.editor = editor;
49315         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49316         var editorcore = this.editorcore;
49317         
49318         var fid = editorcore.frameId;
49319         var etb = this;
49320         function btn(id, toggle, handler){
49321             var xid = fid + '-'+ id ;
49322             return {
49323                 id : xid,
49324                 cmd : id,
49325                 cls : 'x-btn-icon x-edit-'+id,
49326                 enableToggle:toggle !== false,
49327                 scope: editorcore, // was editor...
49328                 handler:handler||editorcore.relayBtnCmd,
49329                 clickEvent:'mousedown',
49330                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49331                 tabIndex:-1
49332             };
49333         }
49334         // create a new element.
49335         var wdiv = editor.wrap.createChild({
49336                 tag: 'div'
49337             }, editor.wrap.dom.firstChild.nextSibling, true);
49338         
49339         // can we do this more than once??
49340         
49341          // stop form submits
49342       
49343  
49344         // disable everything...
49345         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49346         this.toolbars = {};
49347            
49348         for (var i in  ty) {
49349           
49350             this.toolbars[i] = this.buildToolbar(ty[i],i);
49351         }
49352         this.tb = this.toolbars.BODY;
49353         this.tb.el.show();
49354         this.buildFooter();
49355         this.footer.show();
49356         editor.on('hide', function( ) { this.footer.hide() }, this);
49357         editor.on('show', function( ) { this.footer.show() }, this);
49358         
49359          
49360         this.rendered = true;
49361         
49362         // the all the btns;
49363         editor.on('editorevent', this.updateToolbar, this);
49364         // other toolbars need to implement this..
49365         //editor.on('editmodechange', this.updateToolbar, this);
49366     },
49367     
49368     
49369     
49370     /**
49371      * Protected method that will not generally be called directly. It triggers
49372      * a toolbar update by reading the markup state of the current selection in the editor.
49373      *
49374      * Note you can force an update by calling on('editorevent', scope, false)
49375      */
49376     updateToolbar: function(editor ,ev, sel)
49377     {
49378         
49379         if (ev) {
49380             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49381         }
49382         
49383         //Roo.log(ev);
49384         // capture mouse up - this is handy for selecting images..
49385         // perhaps should go somewhere else...
49386         if(!this.editorcore.activated){
49387              this.editor.onFirstFocus();
49388             return;
49389         }
49390         Roo.log(ev ? ev.target : 'NOTARGET');
49391         
49392         
49393         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49394         // selectNode - might want to handle IE?
49395         
49396         
49397         
49398         if (ev &&
49399             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49400             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49401             // they have click on an image...
49402             // let's see if we can change the selection...
49403             sel = ev.target;
49404             
49405             // this triggers looping?
49406             //this.editorcore.selectNode(sel);
49407              
49408         }  
49409         
49410       
49411         //var updateFooter = sel ? false : true; 
49412         
49413         
49414         var ans = this.editorcore.getAllAncestors();
49415         
49416         // pick
49417         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49418         
49419         if (!sel) { 
49420             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49421             sel = sel ? sel : this.editorcore.doc.body;
49422             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49423             
49424         }
49425         
49426         var tn = sel.tagName.toUpperCase();
49427         var lastSel = this.tb.selectedNode;
49428         this.tb.selectedNode = sel;
49429         var left_label = tn;
49430         
49431         // ok see if we are editing a block?
49432         var sel_el = Roo.get(sel);
49433         var db = false;
49434         // you are not actually selecting the block.
49435         if (sel && sel.hasAttribute('data-block')) {
49436             db = sel;
49437         } else if (sel && !sel.hasAttribute('contenteditable')) {
49438             db = sel_el.findParent('[data-block]');
49439             var cepar = sel_el.findParent('[contenteditable=true]');
49440             if (db && cepar && cepar.tagName != 'BODY') {
49441                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49442             }   
49443         }
49444         
49445         
49446         var block = false;
49447         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49448         if (db) {
49449             block = Roo.htmleditor.Block.factory(db);
49450             if (block) {
49451                 tn = 'BLOCK.' + db.getAttribute('data-block');
49452                 
49453                 //this.editorcore.selectNode(db);
49454                 if (typeof(this.toolbars[tn]) == 'undefined') {
49455                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49456                 }
49457                 this.toolbars[tn].selectedNode = db;
49458                 left_label = block.friendly_name;
49459                 ans = this.editorcore.getAllAncestors();
49460             }
49461             
49462                 
49463             
49464         }
49465         
49466         
49467         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49468             return; // no change?
49469         }
49470         
49471         
49472           
49473         this.tb.el.hide();
49474         ///console.log("show: " + tn);
49475         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49476         
49477         this.tb.el.show();
49478         // update name
49479         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49480         
49481         
49482         // update attributes
49483         if (block) {
49484              
49485             this.tb.fields.each(function(e) {
49486                 e.setValue(block[e.name]);
49487             });
49488             
49489             
49490         } else  if (this.tb.fields && this.tb.selectedNode) {
49491             this.tb.fields.each( function(e) {
49492                 if (e.stylename) {
49493                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49494                     return;
49495                 } 
49496                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49497             }, this);
49498             this.updateToolbarStyles(this.tb.selectedNode);  
49499         }
49500         
49501         
49502        
49503         Roo.menu.MenuMgr.hideAll();
49504
49505         
49506         
49507     
49508         // update the footer
49509         //
49510         this.updateFooter(ans);
49511              
49512     },
49513     
49514     updateToolbarStyles : function(sel)
49515     {
49516         var hasStyles = false;
49517         for(var i in this.styles) {
49518             hasStyles = true;
49519             break;
49520         }
49521         
49522         // update styles
49523         if (hasStyles && this.tb.hasStyles) { 
49524             var st = this.tb.fields.item(0);
49525             
49526             st.store.removeAll();
49527             var cn = sel.className.split(/\s+/);
49528             
49529             var avs = [];
49530             if (this.styles['*']) {
49531                 
49532                 Roo.each(this.styles['*'], function(v) {
49533                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49534                 });
49535             }
49536             if (this.styles[tn]) { 
49537                 Roo.each(this.styles[tn], function(v) {
49538                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49539                 });
49540             }
49541             
49542             st.store.loadData(avs);
49543             st.collapse();
49544             st.setValue(cn);
49545         }
49546     },
49547     
49548      
49549     updateFooter : function(ans)
49550     {
49551         var html = '';
49552         if (ans === false) {
49553             this.footDisp.dom.innerHTML = '';
49554             return;
49555         }
49556         
49557         this.footerEls = ans.reverse();
49558         Roo.each(this.footerEls, function(a,i) {
49559             if (!a) { return; }
49560             html += html.length ? ' &gt; '  :  '';
49561             
49562             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49563             
49564         });
49565        
49566         // 
49567         var sz = this.footDisp.up('td').getSize();
49568         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49569         this.footDisp.dom.style.marginLeft = '5px';
49570         
49571         this.footDisp.dom.style.overflow = 'hidden';
49572         
49573         this.footDisp.dom.innerHTML = html;
49574             
49575         
49576     },
49577    
49578        
49579     // private
49580     onDestroy : function(){
49581         if(this.rendered){
49582             
49583             this.tb.items.each(function(item){
49584                 if(item.menu){
49585                     item.menu.removeAll();
49586                     if(item.menu.el){
49587                         item.menu.el.destroy();
49588                     }
49589                 }
49590                 item.destroy();
49591             });
49592              
49593         }
49594     },
49595     onFirstFocus: function() {
49596         // need to do this for all the toolbars..
49597         this.tb.items.each(function(item){
49598            item.enable();
49599         });
49600     },
49601     buildToolbar: function(tlist, nm, friendly_name, block)
49602     {
49603         var editor = this.editor;
49604         var editorcore = this.editorcore;
49605          // create a new element.
49606         var wdiv = editor.wrap.createChild({
49607                 tag: 'div'
49608             }, editor.wrap.dom.firstChild.nextSibling, true);
49609         
49610        
49611         var tb = new Roo.Toolbar(wdiv);
49612         this.tb = tb;
49613         if (tlist === false && block) {
49614             tlist = block.contextMenu(this);
49615         }
49616         
49617         tb.hasStyles = false;
49618         tb.name = nm;
49619         
49620         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49621         
49622         var styles = Array.from(this.styles);
49623         
49624         
49625         // styles...
49626         if (styles && styles.length) {
49627             tb.hasStyles = true;
49628             // this needs a multi-select checkbox...
49629             tb.addField( new Roo.form.ComboBox({
49630                 store: new Roo.data.SimpleStore({
49631                     id : 'val',
49632                     fields: ['val', 'selected'],
49633                     data : [] 
49634                 }),
49635                 name : '-roo-edit-className',
49636                 attrname : 'className',
49637                 displayField: 'val',
49638                 typeAhead: false,
49639                 mode: 'local',
49640                 editable : false,
49641                 triggerAction: 'all',
49642                 emptyText:'Select Style',
49643                 selectOnFocus:true,
49644                 width: 130,
49645                 listeners : {
49646                     'select': function(c, r, i) {
49647                         // initial support only for on class per el..
49648                         tb.selectedNode.className =  r ? r.get('val') : '';
49649                         editorcore.syncValue();
49650                     }
49651                 }
49652     
49653             }));
49654         }
49655         
49656         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49657         
49658         
49659         for (var i = 0; i < tlist.length; i++) {
49660             
49661             // newer versions will use xtype cfg to create menus.
49662             if (typeof(tlist[i].xtype) != 'undefined') {
49663                 
49664                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49665                 
49666                 
49667                 continue;
49668             }
49669             
49670             var item = tlist[i];
49671             tb.add(item.title + ":&nbsp;");
49672             
49673             
49674             //optname == used so you can configure the options available..
49675             var opts = item.opts ? item.opts : false;
49676             if (item.optname) { // use the b
49677                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49678            
49679             }
49680             
49681             if (opts) {
49682                 // opts == pulldown..
49683                 tb.addField( new Roo.form.ComboBox({
49684                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49685                         id : 'val',
49686                         fields: ['val', 'display'],
49687                         data : opts  
49688                     }),
49689                     name : '-roo-edit-' + tlist[i].name,
49690                     
49691                     attrname : tlist[i].name,
49692                     stylename : item.style ? item.style : false,
49693                     
49694                     displayField: item.displayField ? item.displayField : 'val',
49695                     valueField :  'val',
49696                     typeAhead: false,
49697                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
49698                     editable : false,
49699                     triggerAction: 'all',
49700                     emptyText:'Select',
49701                     selectOnFocus:true,
49702                     width: item.width ? item.width  : 130,
49703                     listeners : {
49704                         'select': function(c, r, i) {
49705                             if (tb.selectedNode.hasAttribute('data-block')) {
49706                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49707                                 b[c.attrname] = r.get('val');
49708                                 b.updateElement(tb.selectedNode);
49709                                 editorcore.syncValue();
49710                                 return;
49711                             }
49712                             
49713                             if (c.stylename) {
49714                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49715                                 editorcore.syncValue();
49716                                 return;
49717                             }
49718                             if (r === false) {
49719                                 tb.selectedNode.removeAttribute(c.attrname);
49720                                 editorcore.syncValue();
49721                                 return;
49722                             }
49723                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49724                             editorcore.syncValue();
49725                         }
49726                     }
49727
49728                 }));
49729                 continue;
49730                     
49731                  
49732                 /*
49733                 tb.addField( new Roo.form.TextField({
49734                     name: i,
49735                     width: 100,
49736                     //allowBlank:false,
49737                     value: ''
49738                 }));
49739                 continue;
49740                 */
49741             }
49742             tb.addField( new Roo.form.TextField({
49743                 name: '-roo-edit-' + tlist[i].name,
49744                 attrname : tlist[i].name,
49745                 
49746                 width: item.width,
49747                 //allowBlank:true,
49748                 value: '',
49749                 listeners: {
49750                     'change' : function(f, nv, ov) {
49751                         
49752                         if (tb.selectedNode.hasAttribute('data-block')) {
49753                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49754                             b[f.attrname] = nv;
49755                             b.updateElement(tb.selectedNode);
49756                             editorcore.syncValue();
49757                             return;
49758                         }
49759                         
49760                         tb.selectedNode.setAttribute(f.attrname, nv);
49761                         editorcore.syncValue();
49762                     }
49763                 }
49764             }));
49765              
49766         }
49767         
49768         var _this = this;
49769         
49770         if(nm == 'BODY'){
49771             tb.addSeparator();
49772         
49773             tb.addButton( {
49774                 text: 'Stylesheets',
49775
49776                 listeners : {
49777                     click : function ()
49778                     {
49779                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49780                     }
49781                 }
49782             });
49783         }
49784         
49785         tb.addFill();
49786         tb.addButton({
49787             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49788     
49789             listeners : {
49790                 click : function ()
49791                 {
49792                     var sn = tb.selectedNode;
49793                     if (block) {
49794                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removalNode();
49795                         
49796                     }
49797                     if (!sn) {
49798                         return;
49799                     }
49800                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49801                     if (sn.hasAttribute('data-block')) {
49802                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49803                         sn.parentNode.removeChild(sn);
49804                         
49805                     } else if (sn && sn.tagName != 'BODY') {
49806                         // remove and keep parents.
49807                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49808                         a.removeTag(sn);
49809                     }
49810                     
49811                     
49812                     var range = editorcore.createRange();
49813         
49814                     range.setStart(stn,0);
49815                     range.setEnd(stn,0); 
49816                     var selection = editorcore.getSelection();
49817                     selection.removeAllRanges();
49818                     selection.addRange(range);
49819                     
49820                     
49821                     //_this.updateToolbar(null, null, pn);
49822                     _this.updateToolbar(null, null, null);
49823                     _this.updateFooter(false);
49824                     
49825                 }
49826             }
49827             
49828                     
49829                 
49830             
49831         });
49832         
49833         
49834         tb.el.on('click', function(e){
49835             e.preventDefault(); // what does this do?
49836         });
49837         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49838         tb.el.hide();
49839         
49840         // dont need to disable them... as they will get hidden
49841         return tb;
49842          
49843         
49844     },
49845     buildFooter : function()
49846     {
49847         
49848         var fel = this.editor.wrap.createChild();
49849         this.footer = new Roo.Toolbar(fel);
49850         // toolbar has scrolly on left / right?
49851         var footDisp= new Roo.Toolbar.Fill();
49852         var _t = this;
49853         this.footer.add(
49854             {
49855                 text : '&lt;',
49856                 xtype: 'Button',
49857                 handler : function() {
49858                     _t.footDisp.scrollTo('left',0,true)
49859                 }
49860             }
49861         );
49862         this.footer.add( footDisp );
49863         this.footer.add( 
49864             {
49865                 text : '&gt;',
49866                 xtype: 'Button',
49867                 handler : function() {
49868                     // no animation..
49869                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49870                 }
49871             }
49872         );
49873         var fel = Roo.get(footDisp.el);
49874         fel.addClass('x-editor-context');
49875         this.footDispWrap = fel; 
49876         this.footDispWrap.overflow  = 'hidden';
49877         
49878         this.footDisp = fel.createChild();
49879         this.footDispWrap.on('click', this.onContextClick, this)
49880         
49881         
49882     },
49883     // when the footer contect changes
49884     onContextClick : function (ev,dom)
49885     {
49886         ev.preventDefault();
49887         var  cn = dom.className;
49888         //Roo.log(cn);
49889         if (!cn.match(/x-ed-loc-/)) {
49890             return;
49891         }
49892         var n = cn.split('-').pop();
49893         var ans = this.footerEls;
49894         var sel = ans[n];
49895         
49896          // pick
49897         var range = this.editorcore.createRange();
49898         
49899         range.selectNodeContents(sel);
49900         //range.selectNode(sel);
49901         
49902         
49903         var selection = this.editorcore.getSelection();
49904         selection.removeAllRanges();
49905         selection.addRange(range);
49906         
49907         
49908         
49909         this.updateToolbar(null, null, sel);
49910         
49911         
49912     }
49913     
49914     
49915     
49916     
49917     
49918 });
49919
49920
49921
49922
49923
49924 /*
49925  * Based on:
49926  * Ext JS Library 1.1.1
49927  * Copyright(c) 2006-2007, Ext JS, LLC.
49928  *
49929  * Originally Released Under LGPL - original licence link has changed is not relivant.
49930  *
49931  * Fork - LGPL
49932  * <script type="text/javascript">
49933  */
49934  
49935 /**
49936  * @class Roo.form.BasicForm
49937  * @extends Roo.util.Observable
49938  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49939  * @constructor
49940  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49941  * @param {Object} config Configuration options
49942  */
49943 Roo.form.BasicForm = function(el, config){
49944     this.allItems = [];
49945     this.childForms = [];
49946     Roo.apply(this, config);
49947     /*
49948      * The Roo.form.Field items in this form.
49949      * @type MixedCollection
49950      */
49951      
49952      
49953     this.items = new Roo.util.MixedCollection(false, function(o){
49954         return o.id || (o.id = Roo.id());
49955     });
49956     this.addEvents({
49957         /**
49958          * @event beforeaction
49959          * Fires before any action is performed. Return false to cancel the action.
49960          * @param {Form} this
49961          * @param {Action} action The action to be performed
49962          */
49963         beforeaction: true,
49964         /**
49965          * @event actionfailed
49966          * Fires when an action fails.
49967          * @param {Form} this
49968          * @param {Action} action The action that failed
49969          */
49970         actionfailed : true,
49971         /**
49972          * @event actioncomplete
49973          * Fires when an action is completed.
49974          * @param {Form} this
49975          * @param {Action} action The action that completed
49976          */
49977         actioncomplete : true
49978     });
49979     if(el){
49980         this.initEl(el);
49981     }
49982     Roo.form.BasicForm.superclass.constructor.call(this);
49983     
49984     Roo.form.BasicForm.popover.apply();
49985 };
49986
49987 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
49988     /**
49989      * @cfg {String} method
49990      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
49991      */
49992     /**
49993      * @cfg {DataReader} reader
49994      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
49995      * This is optional as there is built-in support for processing JSON.
49996      */
49997     /**
49998      * @cfg {DataReader} errorReader
49999      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50000      * This is completely optional as there is built-in support for processing JSON.
50001      */
50002     /**
50003      * @cfg {String} url
50004      * The URL to use for form actions if one isn't supplied in the action options.
50005      */
50006     /**
50007      * @cfg {Boolean} fileUpload
50008      * Set to true if this form is a file upload.
50009      */
50010      
50011     /**
50012      * @cfg {Object} baseParams
50013      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50014      */
50015      /**
50016      
50017     /**
50018      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50019      */
50020     timeout: 30,
50021
50022     // private
50023     activeAction : null,
50024
50025     /**
50026      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50027      * or setValues() data instead of when the form was first created.
50028      */
50029     trackResetOnLoad : false,
50030     
50031     
50032     /**
50033      * childForms - used for multi-tab forms
50034      * @type {Array}
50035      */
50036     childForms : false,
50037     
50038     /**
50039      * allItems - full list of fields.
50040      * @type {Array}
50041      */
50042     allItems : false,
50043     
50044     /**
50045      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50046      * element by passing it or its id or mask the form itself by passing in true.
50047      * @type Mixed
50048      */
50049     waitMsgTarget : false,
50050     
50051     /**
50052      * @type Boolean
50053      */
50054     disableMask : false,
50055     
50056     /**
50057      * @cfg {Boolean} errorMask (true|false) default false
50058      */
50059     errorMask : false,
50060     
50061     /**
50062      * @cfg {Number} maskOffset Default 100
50063      */
50064     maskOffset : 100,
50065
50066     // private
50067     initEl : function(el){
50068         this.el = Roo.get(el);
50069         this.id = this.el.id || Roo.id();
50070         this.el.on('submit', this.onSubmit, this);
50071         this.el.addClass('x-form');
50072     },
50073
50074     // private
50075     onSubmit : function(e){
50076         e.stopEvent();
50077     },
50078
50079     /**
50080      * Returns true if client-side validation on the form is successful.
50081      * @return Boolean
50082      */
50083     isValid : function(){
50084         var valid = true;
50085         var target = false;
50086         this.items.each(function(f){
50087             if(f.validate()){
50088                 return;
50089             }
50090             
50091             valid = false;
50092                 
50093             if(!target && f.el.isVisible(true)){
50094                 target = f;
50095             }
50096         });
50097         
50098         if(this.errorMask && !valid){
50099             Roo.form.BasicForm.popover.mask(this, target);
50100         }
50101         
50102         return valid;
50103     },
50104     /**
50105      * Returns array of invalid form fields.
50106      * @return Array
50107      */
50108     
50109     invalidFields : function()
50110     {
50111         var ret = [];
50112         this.items.each(function(f){
50113             if(f.validate()){
50114                 return;
50115             }
50116             ret.push(f);
50117             
50118         });
50119         
50120         return ret;
50121     },
50122     
50123     
50124     /**
50125      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50126      * @return Boolean
50127      */
50128     isDirty : function(){
50129         var dirty = false;
50130         this.items.each(function(f){
50131            if(f.isDirty()){
50132                dirty = true;
50133                return false;
50134            }
50135         });
50136         return dirty;
50137     },
50138     
50139     /**
50140      * Returns true if any fields in this form have changed since their original load. (New version)
50141      * @return Boolean
50142      */
50143     
50144     hasChanged : function()
50145     {
50146         var dirty = false;
50147         this.items.each(function(f){
50148            if(f.hasChanged()){
50149                dirty = true;
50150                return false;
50151            }
50152         });
50153         return dirty;
50154         
50155     },
50156     /**
50157      * Resets all hasChanged to 'false' -
50158      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50159      * So hasChanged storage is only to be used for this purpose
50160      * @return Boolean
50161      */
50162     resetHasChanged : function()
50163     {
50164         this.items.each(function(f){
50165            f.resetHasChanged();
50166         });
50167         
50168     },
50169     
50170     
50171     /**
50172      * Performs a predefined action (submit or load) or custom actions you define on this form.
50173      * @param {String} actionName The name of the action type
50174      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50175      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50176      * accept other config options):
50177      * <pre>
50178 Property          Type             Description
50179 ----------------  ---------------  ----------------------------------------------------------------------------------
50180 url               String           The url for the action (defaults to the form's url)
50181 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50182 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50183 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50184                                    validate the form on the client (defaults to false)
50185      * </pre>
50186      * @return {BasicForm} this
50187      */
50188     doAction : function(action, options){
50189         if(typeof action == 'string'){
50190             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50191         }
50192         if(this.fireEvent('beforeaction', this, action) !== false){
50193             this.beforeAction(action);
50194             action.run.defer(100, action);
50195         }
50196         return this;
50197     },
50198
50199     /**
50200      * Shortcut to do a submit action.
50201      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50202      * @return {BasicForm} this
50203      */
50204     submit : function(options){
50205         this.doAction('submit', options);
50206         return this;
50207     },
50208
50209     /**
50210      * Shortcut to do a load action.
50211      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50212      * @return {BasicForm} this
50213      */
50214     load : function(options){
50215         this.doAction('load', options);
50216         return this;
50217     },
50218
50219     /**
50220      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50221      * @param {Record} record The record to edit
50222      * @return {BasicForm} this
50223      */
50224     updateRecord : function(record){
50225         record.beginEdit();
50226         var fs = record.fields;
50227         fs.each(function(f){
50228             var field = this.findField(f.name);
50229             if(field){
50230                 record.set(f.name, field.getValue());
50231             }
50232         }, this);
50233         record.endEdit();
50234         return this;
50235     },
50236
50237     /**
50238      * Loads an Roo.data.Record into this form.
50239      * @param {Record} record The record to load
50240      * @return {BasicForm} this
50241      */
50242     loadRecord : function(record){
50243         this.setValues(record.data);
50244         return this;
50245     },
50246
50247     // private
50248     beforeAction : function(action){
50249         var o = action.options;
50250         
50251         if(!this.disableMask) {
50252             if(this.waitMsgTarget === true){
50253                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50254             }else if(this.waitMsgTarget){
50255                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50256                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50257             }else {
50258                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50259             }
50260         }
50261         
50262          
50263     },
50264
50265     // private
50266     afterAction : function(action, success){
50267         this.activeAction = null;
50268         var o = action.options;
50269         
50270         if(!this.disableMask) {
50271             if(this.waitMsgTarget === true){
50272                 this.el.unmask();
50273             }else if(this.waitMsgTarget){
50274                 this.waitMsgTarget.unmask();
50275             }else{
50276                 Roo.MessageBox.updateProgress(1);
50277                 Roo.MessageBox.hide();
50278             }
50279         }
50280         
50281         if(success){
50282             if(o.reset){
50283                 this.reset();
50284             }
50285             Roo.callback(o.success, o.scope, [this, action]);
50286             this.fireEvent('actioncomplete', this, action);
50287             
50288         }else{
50289             
50290             // failure condition..
50291             // we have a scenario where updates need confirming.
50292             // eg. if a locking scenario exists..
50293             // we look for { errors : { needs_confirm : true }} in the response.
50294             if (
50295                 (typeof(action.result) != 'undefined')  &&
50296                 (typeof(action.result.errors) != 'undefined')  &&
50297                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50298            ){
50299                 var _t = this;
50300                 Roo.MessageBox.confirm(
50301                     "Change requires confirmation",
50302                     action.result.errorMsg,
50303                     function(r) {
50304                         if (r != 'yes') {
50305                             return;
50306                         }
50307                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50308                     }
50309                     
50310                 );
50311                 
50312                 
50313                 
50314                 return;
50315             }
50316             
50317             Roo.callback(o.failure, o.scope, [this, action]);
50318             // show an error message if no failed handler is set..
50319             if (!this.hasListener('actionfailed')) {
50320                 Roo.MessageBox.alert("Error",
50321                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50322                         action.result.errorMsg :
50323                         "Saving Failed, please check your entries or try again"
50324                 );
50325             }
50326             
50327             this.fireEvent('actionfailed', this, action);
50328         }
50329         
50330     },
50331
50332     /**
50333      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50334      * @param {String} id The value to search for
50335      * @return Field
50336      */
50337     findField : function(id){
50338         var field = this.items.get(id);
50339         if(!field){
50340             this.items.each(function(f){
50341                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50342                     field = f;
50343                     return false;
50344                 }
50345             });
50346         }
50347         return field || null;
50348     },
50349
50350     /**
50351      * Add a secondary form to this one, 
50352      * Used to provide tabbed forms. One form is primary, with hidden values 
50353      * which mirror the elements from the other forms.
50354      * 
50355      * @param {Roo.form.Form} form to add.
50356      * 
50357      */
50358     addForm : function(form)
50359     {
50360        
50361         if (this.childForms.indexOf(form) > -1) {
50362             // already added..
50363             return;
50364         }
50365         this.childForms.push(form);
50366         var n = '';
50367         Roo.each(form.allItems, function (fe) {
50368             
50369             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50370             if (this.findField(n)) { // already added..
50371                 return;
50372             }
50373             var add = new Roo.form.Hidden({
50374                 name : n
50375             });
50376             add.render(this.el);
50377             
50378             this.add( add );
50379         }, this);
50380         
50381     },
50382     /**
50383      * Mark fields in this form invalid in bulk.
50384      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50385      * @return {BasicForm} this
50386      */
50387     markInvalid : function(errors){
50388         if(errors instanceof Array){
50389             for(var i = 0, len = errors.length; i < len; i++){
50390                 var fieldError = errors[i];
50391                 var f = this.findField(fieldError.id);
50392                 if(f){
50393                     f.markInvalid(fieldError.msg);
50394                 }
50395             }
50396         }else{
50397             var field, id;
50398             for(id in errors){
50399                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50400                     field.markInvalid(errors[id]);
50401                 }
50402             }
50403         }
50404         Roo.each(this.childForms || [], function (f) {
50405             f.markInvalid(errors);
50406         });
50407         
50408         return this;
50409     },
50410
50411     /**
50412      * Set values for fields in this form in bulk.
50413      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50414      * @return {BasicForm} this
50415      */
50416     setValues : function(values){
50417         if(values instanceof Array){ // array of objects
50418             for(var i = 0, len = values.length; i < len; i++){
50419                 var v = values[i];
50420                 var f = this.findField(v.id);
50421                 if(f){
50422                     f.setValue(v.value);
50423                     if(this.trackResetOnLoad){
50424                         f.originalValue = f.getValue();
50425                     }
50426                 }
50427             }
50428         }else{ // object hash
50429             var field, id;
50430             for(id in values){
50431                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50432                     
50433                     if (field.setFromData && 
50434                         field.valueField && 
50435                         field.displayField &&
50436                         // combos' with local stores can 
50437                         // be queried via setValue()
50438                         // to set their value..
50439                         (field.store && !field.store.isLocal)
50440                         ) {
50441                         // it's a combo
50442                         var sd = { };
50443                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50444                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50445                         field.setFromData(sd);
50446                         
50447                     } else {
50448                         field.setValue(values[id]);
50449                     }
50450                     
50451                     
50452                     if(this.trackResetOnLoad){
50453                         field.originalValue = field.getValue();
50454                     }
50455                 }
50456             }
50457         }
50458         this.resetHasChanged();
50459         
50460         
50461         Roo.each(this.childForms || [], function (f) {
50462             f.setValues(values);
50463             f.resetHasChanged();
50464         });
50465                 
50466         return this;
50467     },
50468  
50469     /**
50470      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50471      * they are returned as an array.
50472      * @param {Boolean} asString
50473      * @return {Object}
50474      */
50475     getValues : function(asString)
50476     {
50477         if (this.childForms) {
50478             // copy values from the child forms
50479             Roo.each(this.childForms, function (f) {
50480                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50481             }, this);
50482         }
50483         
50484         // use formdata
50485         if (typeof(FormData) != 'undefined' && asString !== true) {
50486             // this relies on a 'recent' version of chrome apparently...
50487             try {
50488                 var fd = (new FormData(this.el.dom)).entries();
50489                 var ret = {};
50490                 var ent = fd.next();
50491                 while (!ent.done) {
50492                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50493                     ent = fd.next();
50494                 };
50495                 return ret;
50496             } catch(e) {
50497                 
50498             }
50499             
50500         }
50501         
50502         
50503         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50504         if(asString === true){
50505             return fs;
50506         }
50507         return Roo.urlDecode(fs);
50508     },
50509     
50510     /**
50511      * Returns the fields in this form as an object with key/value pairs. 
50512      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50513      * @return {Object}
50514      */
50515     getFieldValues : function(with_hidden)
50516     {
50517         if (this.childForms) {
50518             // copy values from the child forms
50519             // should this call getFieldValues - probably not as we do not currently copy
50520             // hidden fields when we generate..
50521             Roo.each(this.childForms, function (f) {
50522                 this.setValues(f.getFieldValues());
50523             }, this);
50524         }
50525         
50526         var ret = {};
50527         this.items.each(function(f){
50528             if (!f.getName()) {
50529                 return;
50530             }
50531             var v = f.getValue();
50532             if (f.inputType =='radio') {
50533                 if (typeof(ret[f.getName()]) == 'undefined') {
50534                     ret[f.getName()] = ''; // empty..
50535                 }
50536                 
50537                 if (!f.el.dom.checked) {
50538                     return;
50539                     
50540                 }
50541                 v = f.el.dom.value;
50542                 
50543             }
50544             
50545             // not sure if this supported any more..
50546             if ((typeof(v) == 'object') && f.getRawValue) {
50547                 v = f.getRawValue() ; // dates..
50548             }
50549             // combo boxes where name != hiddenName...
50550             if (f.name != f.getName()) {
50551                 ret[f.name] = f.getRawValue();
50552             }
50553             ret[f.getName()] = v;
50554         });
50555         
50556         return ret;
50557     },
50558
50559     /**
50560      * Clears all invalid messages in this form.
50561      * @return {BasicForm} this
50562      */
50563     clearInvalid : function(){
50564         this.items.each(function(f){
50565            f.clearInvalid();
50566         });
50567         
50568         Roo.each(this.childForms || [], function (f) {
50569             f.clearInvalid();
50570         });
50571         
50572         
50573         return this;
50574     },
50575
50576     /**
50577      * Resets this form.
50578      * @return {BasicForm} this
50579      */
50580     reset : function(){
50581         this.items.each(function(f){
50582             f.reset();
50583         });
50584         
50585         Roo.each(this.childForms || [], function (f) {
50586             f.reset();
50587         });
50588         this.resetHasChanged();
50589         
50590         return this;
50591     },
50592
50593     /**
50594      * Add Roo.form components to this form.
50595      * @param {Field} field1
50596      * @param {Field} field2 (optional)
50597      * @param {Field} etc (optional)
50598      * @return {BasicForm} this
50599      */
50600     add : function(){
50601         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50602         return this;
50603     },
50604
50605
50606     /**
50607      * Removes a field from the items collection (does NOT remove its markup).
50608      * @param {Field} field
50609      * @return {BasicForm} this
50610      */
50611     remove : function(field){
50612         this.items.remove(field);
50613         return this;
50614     },
50615
50616     /**
50617      * Looks at the fields in this form, checks them for an id attribute,
50618      * and calls applyTo on the existing dom element with that id.
50619      * @return {BasicForm} this
50620      */
50621     render : function(){
50622         this.items.each(function(f){
50623             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50624                 f.applyTo(f.id);
50625             }
50626         });
50627         return this;
50628     },
50629
50630     /**
50631      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50632      * @param {Object} values
50633      * @return {BasicForm} this
50634      */
50635     applyToFields : function(o){
50636         this.items.each(function(f){
50637            Roo.apply(f, o);
50638         });
50639         return this;
50640     },
50641
50642     /**
50643      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50644      * @param {Object} values
50645      * @return {BasicForm} this
50646      */
50647     applyIfToFields : function(o){
50648         this.items.each(function(f){
50649            Roo.applyIf(f, o);
50650         });
50651         return this;
50652     }
50653 });
50654
50655 // back compat
50656 Roo.BasicForm = Roo.form.BasicForm;
50657
50658 Roo.apply(Roo.form.BasicForm, {
50659     
50660     popover : {
50661         
50662         padding : 5,
50663         
50664         isApplied : false,
50665         
50666         isMasked : false,
50667         
50668         form : false,
50669         
50670         target : false,
50671         
50672         intervalID : false,
50673         
50674         maskEl : false,
50675         
50676         apply : function()
50677         {
50678             if(this.isApplied){
50679                 return;
50680             }
50681             
50682             this.maskEl = {
50683                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50684                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50685                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50686                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50687             };
50688             
50689             this.maskEl.top.enableDisplayMode("block");
50690             this.maskEl.left.enableDisplayMode("block");
50691             this.maskEl.bottom.enableDisplayMode("block");
50692             this.maskEl.right.enableDisplayMode("block");
50693             
50694             Roo.get(document.body).on('click', function(){
50695                 this.unmask();
50696             }, this);
50697             
50698             Roo.get(document.body).on('touchstart', function(){
50699                 this.unmask();
50700             }, this);
50701             
50702             this.isApplied = true
50703         },
50704         
50705         mask : function(form, target)
50706         {
50707             this.form = form;
50708             
50709             this.target = target;
50710             
50711             if(!this.form.errorMask || !target.el){
50712                 return;
50713             }
50714             
50715             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50716             
50717             var ot = this.target.el.calcOffsetsTo(scrollable);
50718             
50719             var scrollTo = ot[1] - this.form.maskOffset;
50720             
50721             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50722             
50723             scrollable.scrollTo('top', scrollTo);
50724             
50725             var el = this.target.wrap || this.target.el;
50726             
50727             var box = el.getBox();
50728             
50729             this.maskEl.top.setStyle('position', 'absolute');
50730             this.maskEl.top.setStyle('z-index', 10000);
50731             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50732             this.maskEl.top.setLeft(0);
50733             this.maskEl.top.setTop(0);
50734             this.maskEl.top.show();
50735             
50736             this.maskEl.left.setStyle('position', 'absolute');
50737             this.maskEl.left.setStyle('z-index', 10000);
50738             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50739             this.maskEl.left.setLeft(0);
50740             this.maskEl.left.setTop(box.y - this.padding);
50741             this.maskEl.left.show();
50742
50743             this.maskEl.bottom.setStyle('position', 'absolute');
50744             this.maskEl.bottom.setStyle('z-index', 10000);
50745             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50746             this.maskEl.bottom.setLeft(0);
50747             this.maskEl.bottom.setTop(box.bottom + this.padding);
50748             this.maskEl.bottom.show();
50749
50750             this.maskEl.right.setStyle('position', 'absolute');
50751             this.maskEl.right.setStyle('z-index', 10000);
50752             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50753             this.maskEl.right.setLeft(box.right + this.padding);
50754             this.maskEl.right.setTop(box.y - this.padding);
50755             this.maskEl.right.show();
50756
50757             this.intervalID = window.setInterval(function() {
50758                 Roo.form.BasicForm.popover.unmask();
50759             }, 10000);
50760
50761             window.onwheel = function(){ return false;};
50762             
50763             (function(){ this.isMasked = true; }).defer(500, this);
50764             
50765         },
50766         
50767         unmask : function()
50768         {
50769             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50770                 return;
50771             }
50772             
50773             this.maskEl.top.setStyle('position', 'absolute');
50774             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50775             this.maskEl.top.hide();
50776
50777             this.maskEl.left.setStyle('position', 'absolute');
50778             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50779             this.maskEl.left.hide();
50780
50781             this.maskEl.bottom.setStyle('position', 'absolute');
50782             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50783             this.maskEl.bottom.hide();
50784
50785             this.maskEl.right.setStyle('position', 'absolute');
50786             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50787             this.maskEl.right.hide();
50788             
50789             window.onwheel = function(){ return true;};
50790             
50791             if(this.intervalID){
50792                 window.clearInterval(this.intervalID);
50793                 this.intervalID = false;
50794             }
50795             
50796             this.isMasked = false;
50797             
50798         }
50799         
50800     }
50801     
50802 });/*
50803  * Based on:
50804  * Ext JS Library 1.1.1
50805  * Copyright(c) 2006-2007, Ext JS, LLC.
50806  *
50807  * Originally Released Under LGPL - original licence link has changed is not relivant.
50808  *
50809  * Fork - LGPL
50810  * <script type="text/javascript">
50811  */
50812
50813 /**
50814  * @class Roo.form.Form
50815  * @extends Roo.form.BasicForm
50816  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50817  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50818  * @constructor
50819  * @param {Object} config Configuration options
50820  */
50821 Roo.form.Form = function(config){
50822     var xitems =  [];
50823     if (config.items) {
50824         xitems = config.items;
50825         delete config.items;
50826     }
50827    
50828     
50829     Roo.form.Form.superclass.constructor.call(this, null, config);
50830     this.url = this.url || this.action;
50831     if(!this.root){
50832         this.root = new Roo.form.Layout(Roo.applyIf({
50833             id: Roo.id()
50834         }, config));
50835     }
50836     this.active = this.root;
50837     /**
50838      * Array of all the buttons that have been added to this form via {@link addButton}
50839      * @type Array
50840      */
50841     this.buttons = [];
50842     this.allItems = [];
50843     this.addEvents({
50844         /**
50845          * @event clientvalidation
50846          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50847          * @param {Form} this
50848          * @param {Boolean} valid true if the form has passed client-side validation
50849          */
50850         clientvalidation: true,
50851         /**
50852          * @event rendered
50853          * Fires when the form is rendered
50854          * @param {Roo.form.Form} form
50855          */
50856         rendered : true
50857     });
50858     
50859     if (this.progressUrl) {
50860             // push a hidden field onto the list of fields..
50861             this.addxtype( {
50862                     xns: Roo.form, 
50863                     xtype : 'Hidden', 
50864                     name : 'UPLOAD_IDENTIFIER' 
50865             });
50866         }
50867         
50868     
50869     Roo.each(xitems, this.addxtype, this);
50870     
50871 };
50872
50873 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50874      /**
50875      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50876      */
50877     
50878     /**
50879      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50880      */
50881     /**
50882      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50883      */
50884     /**
50885      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50886      */
50887     buttonAlign:'center',
50888
50889     /**
50890      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50891      */
50892     minButtonWidth:75,
50893
50894     /**
50895      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50896      * This property cascades to child containers if not set.
50897      */
50898     labelAlign:'left',
50899
50900     /**
50901      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50902      * fires a looping event with that state. This is required to bind buttons to the valid
50903      * state using the config value formBind:true on the button.
50904      */
50905     monitorValid : false,
50906
50907     /**
50908      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50909      */
50910     monitorPoll : 200,
50911     
50912     /**
50913      * @cfg {String} progressUrl - Url to return progress data 
50914      */
50915     
50916     progressUrl : false,
50917     /**
50918      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50919      * sending a formdata with extra parameters - eg uploaded elements.
50920      */
50921     
50922     formData : false,
50923     
50924     /**
50925      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50926      * fields are added and the column is closed. If no fields are passed the column remains open
50927      * until end() is called.
50928      * @param {Object} config The config to pass to the column
50929      * @param {Field} field1 (optional)
50930      * @param {Field} field2 (optional)
50931      * @param {Field} etc (optional)
50932      * @return Column The column container object
50933      */
50934     column : function(c){
50935         var col = new Roo.form.Column(c);
50936         this.start(col);
50937         if(arguments.length > 1){ // duplicate code required because of Opera
50938             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50939             this.end();
50940         }
50941         return col;
50942     },
50943
50944     /**
50945      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50946      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50947      * until end() is called.
50948      * @param {Object} config The config to pass to the fieldset
50949      * @param {Field} field1 (optional)
50950      * @param {Field} field2 (optional)
50951      * @param {Field} etc (optional)
50952      * @return FieldSet The fieldset container object
50953      */
50954     fieldset : function(c){
50955         var fs = new Roo.form.FieldSet(c);
50956         this.start(fs);
50957         if(arguments.length > 1){ // duplicate code required because of Opera
50958             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50959             this.end();
50960         }
50961         return fs;
50962     },
50963
50964     /**
50965      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50966      * fields are added and the container is closed. If no fields are passed the container remains open
50967      * until end() is called.
50968      * @param {Object} config The config to pass to the Layout
50969      * @param {Field} field1 (optional)
50970      * @param {Field} field2 (optional)
50971      * @param {Field} etc (optional)
50972      * @return Layout The container object
50973      */
50974     container : function(c){
50975         var l = new Roo.form.Layout(c);
50976         this.start(l);
50977         if(arguments.length > 1){ // duplicate code required because of Opera
50978             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50979             this.end();
50980         }
50981         return l;
50982     },
50983
50984     /**
50985      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
50986      * @param {Object} container A Roo.form.Layout or subclass of Layout
50987      * @return {Form} this
50988      */
50989     start : function(c){
50990         // cascade label info
50991         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
50992         this.active.stack.push(c);
50993         c.ownerCt = this.active;
50994         this.active = c;
50995         return this;
50996     },
50997
50998     /**
50999      * Closes the current open container
51000      * @return {Form} this
51001      */
51002     end : function(){
51003         if(this.active == this.root){
51004             return this;
51005         }
51006         this.active = this.active.ownerCt;
51007         return this;
51008     },
51009
51010     /**
51011      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51012      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51013      * as the label of the field.
51014      * @param {Field} field1
51015      * @param {Field} field2 (optional)
51016      * @param {Field} etc. (optional)
51017      * @return {Form} this
51018      */
51019     add : function(){
51020         this.active.stack.push.apply(this.active.stack, arguments);
51021         this.allItems.push.apply(this.allItems,arguments);
51022         var r = [];
51023         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51024             if(a[i].isFormField){
51025                 r.push(a[i]);
51026             }
51027         }
51028         if(r.length > 0){
51029             Roo.form.Form.superclass.add.apply(this, r);
51030         }
51031         return this;
51032     },
51033     
51034
51035     
51036     
51037     
51038      /**
51039      * Find any element that has been added to a form, using it's ID or name
51040      * This can include framesets, columns etc. along with regular fields..
51041      * @param {String} id - id or name to find.
51042      
51043      * @return {Element} e - or false if nothing found.
51044      */
51045     findbyId : function(id)
51046     {
51047         var ret = false;
51048         if (!id) {
51049             return ret;
51050         }
51051         Roo.each(this.allItems, function(f){
51052             if (f.id == id || f.name == id ){
51053                 ret = f;
51054                 return false;
51055             }
51056         });
51057         return ret;
51058     },
51059
51060     
51061     
51062     /**
51063      * Render this form into the passed container. This should only be called once!
51064      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51065      * @return {Form} this
51066      */
51067     render : function(ct)
51068     {
51069         
51070         
51071         
51072         ct = Roo.get(ct);
51073         var o = this.autoCreate || {
51074             tag: 'form',
51075             method : this.method || 'POST',
51076             id : this.id || Roo.id()
51077         };
51078         this.initEl(ct.createChild(o));
51079
51080         this.root.render(this.el);
51081         
51082        
51083              
51084         this.items.each(function(f){
51085             f.render('x-form-el-'+f.id);
51086         });
51087
51088         if(this.buttons.length > 0){
51089             // tables are required to maintain order and for correct IE layout
51090             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51091                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51092                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51093             }}, null, true);
51094             var tr = tb.getElementsByTagName('tr')[0];
51095             for(var i = 0, len = this.buttons.length; i < len; i++) {
51096                 var b = this.buttons[i];
51097                 var td = document.createElement('td');
51098                 td.className = 'x-form-btn-td';
51099                 b.render(tr.appendChild(td));
51100             }
51101         }
51102         if(this.monitorValid){ // initialize after render
51103             this.startMonitoring();
51104         }
51105         this.fireEvent('rendered', this);
51106         return this;
51107     },
51108
51109     /**
51110      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51111      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51112      * object or a valid Roo.DomHelper element config
51113      * @param {Function} handler The function called when the button is clicked
51114      * @param {Object} scope (optional) The scope of the handler function
51115      * @return {Roo.Button}
51116      */
51117     addButton : function(config, handler, scope){
51118         var bc = {
51119             handler: handler,
51120             scope: scope,
51121             minWidth: this.minButtonWidth,
51122             hideParent:true
51123         };
51124         if(typeof config == "string"){
51125             bc.text = config;
51126         }else{
51127             Roo.apply(bc, config);
51128         }
51129         var btn = new Roo.Button(null, bc);
51130         this.buttons.push(btn);
51131         return btn;
51132     },
51133
51134      /**
51135      * Adds a series of form elements (using the xtype property as the factory method.
51136      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51137      * @param {Object} config 
51138      */
51139     
51140     addxtype : function()
51141     {
51142         var ar = Array.prototype.slice.call(arguments, 0);
51143         var ret = false;
51144         for(var i = 0; i < ar.length; i++) {
51145             if (!ar[i]) {
51146                 continue; // skip -- if this happends something invalid got sent, we 
51147                 // should ignore it, as basically that interface element will not show up
51148                 // and that should be pretty obvious!!
51149             }
51150             
51151             if (Roo.form[ar[i].xtype]) {
51152                 ar[i].form = this;
51153                 var fe = Roo.factory(ar[i], Roo.form);
51154                 if (!ret) {
51155                     ret = fe;
51156                 }
51157                 fe.form = this;
51158                 if (fe.store) {
51159                     fe.store.form = this;
51160                 }
51161                 if (fe.isLayout) {  
51162                          
51163                     this.start(fe);
51164                     this.allItems.push(fe);
51165                     if (fe.items && fe.addxtype) {
51166                         fe.addxtype.apply(fe, fe.items);
51167                         delete fe.items;
51168                     }
51169                      this.end();
51170                     continue;
51171                 }
51172                 
51173                 
51174                  
51175                 this.add(fe);
51176               //  console.log('adding ' + ar[i].xtype);
51177             }
51178             if (ar[i].xtype == 'Button') {  
51179                 //console.log('adding button');
51180                 //console.log(ar[i]);
51181                 this.addButton(ar[i]);
51182                 this.allItems.push(fe);
51183                 continue;
51184             }
51185             
51186             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51187                 alert('end is not supported on xtype any more, use items');
51188             //    this.end();
51189             //    //console.log('adding end');
51190             }
51191             
51192         }
51193         return ret;
51194     },
51195     
51196     /**
51197      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51198      * option "monitorValid"
51199      */
51200     startMonitoring : function(){
51201         if(!this.bound){
51202             this.bound = true;
51203             Roo.TaskMgr.start({
51204                 run : this.bindHandler,
51205                 interval : this.monitorPoll || 200,
51206                 scope: this
51207             });
51208         }
51209     },
51210
51211     /**
51212      * Stops monitoring of the valid state of this form
51213      */
51214     stopMonitoring : function(){
51215         this.bound = false;
51216     },
51217
51218     // private
51219     bindHandler : function(){
51220         if(!this.bound){
51221             return false; // stops binding
51222         }
51223         var valid = true;
51224         this.items.each(function(f){
51225             if(!f.isValid(true)){
51226                 valid = false;
51227                 return false;
51228             }
51229         });
51230         for(var i = 0, len = this.buttons.length; i < len; i++){
51231             var btn = this.buttons[i];
51232             if(btn.formBind === true && btn.disabled === valid){
51233                 btn.setDisabled(!valid);
51234             }
51235         }
51236         this.fireEvent('clientvalidation', this, valid);
51237     }
51238     
51239     
51240     
51241     
51242     
51243     
51244     
51245     
51246 });
51247
51248
51249 // back compat
51250 Roo.Form = Roo.form.Form;
51251 /*
51252  * Based on:
51253  * Ext JS Library 1.1.1
51254  * Copyright(c) 2006-2007, Ext JS, LLC.
51255  *
51256  * Originally Released Under LGPL - original licence link has changed is not relivant.
51257  *
51258  * Fork - LGPL
51259  * <script type="text/javascript">
51260  */
51261
51262 // as we use this in bootstrap.
51263 Roo.namespace('Roo.form');
51264  /**
51265  * @class Roo.form.Action
51266  * Internal Class used to handle form actions
51267  * @constructor
51268  * @param {Roo.form.BasicForm} el The form element or its id
51269  * @param {Object} config Configuration options
51270  */
51271
51272  
51273  
51274 // define the action interface
51275 Roo.form.Action = function(form, options){
51276     this.form = form;
51277     this.options = options || {};
51278 };
51279 /**
51280  * Client Validation Failed
51281  * @const 
51282  */
51283 Roo.form.Action.CLIENT_INVALID = 'client';
51284 /**
51285  * Server Validation Failed
51286  * @const 
51287  */
51288 Roo.form.Action.SERVER_INVALID = 'server';
51289  /**
51290  * Connect to Server Failed
51291  * @const 
51292  */
51293 Roo.form.Action.CONNECT_FAILURE = 'connect';
51294 /**
51295  * Reading Data from Server Failed
51296  * @const 
51297  */
51298 Roo.form.Action.LOAD_FAILURE = 'load';
51299
51300 Roo.form.Action.prototype = {
51301     type : 'default',
51302     failureType : undefined,
51303     response : undefined,
51304     result : undefined,
51305
51306     // interface method
51307     run : function(options){
51308
51309     },
51310
51311     // interface method
51312     success : function(response){
51313
51314     },
51315
51316     // interface method
51317     handleResponse : function(response){
51318
51319     },
51320
51321     // default connection failure
51322     failure : function(response){
51323         
51324         this.response = response;
51325         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51326         this.form.afterAction(this, false);
51327     },
51328
51329     processResponse : function(response){
51330         this.response = response;
51331         if(!response.responseText){
51332             return true;
51333         }
51334         this.result = this.handleResponse(response);
51335         return this.result;
51336     },
51337
51338     // utility functions used internally
51339     getUrl : function(appendParams){
51340         var url = this.options.url || this.form.url || this.form.el.dom.action;
51341         if(appendParams){
51342             var p = this.getParams();
51343             if(p){
51344                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51345             }
51346         }
51347         return url;
51348     },
51349
51350     getMethod : function(){
51351         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51352     },
51353
51354     getParams : function(){
51355         var bp = this.form.baseParams;
51356         var p = this.options.params;
51357         if(p){
51358             if(typeof p == "object"){
51359                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51360             }else if(typeof p == 'string' && bp){
51361                 p += '&' + Roo.urlEncode(bp);
51362             }
51363         }else if(bp){
51364             p = Roo.urlEncode(bp);
51365         }
51366         return p;
51367     },
51368
51369     createCallback : function(){
51370         return {
51371             success: this.success,
51372             failure: this.failure,
51373             scope: this,
51374             timeout: (this.form.timeout*1000),
51375             upload: this.form.fileUpload ? this.success : undefined
51376         };
51377     }
51378 };
51379
51380 Roo.form.Action.Submit = function(form, options){
51381     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51382 };
51383
51384 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51385     type : 'submit',
51386
51387     haveProgress : false,
51388     uploadComplete : false,
51389     
51390     // uploadProgress indicator.
51391     uploadProgress : function()
51392     {
51393         if (!this.form.progressUrl) {
51394             return;
51395         }
51396         
51397         if (!this.haveProgress) {
51398             Roo.MessageBox.progress("Uploading", "Uploading");
51399         }
51400         if (this.uploadComplete) {
51401            Roo.MessageBox.hide();
51402            return;
51403         }
51404         
51405         this.haveProgress = true;
51406    
51407         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51408         
51409         var c = new Roo.data.Connection();
51410         c.request({
51411             url : this.form.progressUrl,
51412             params: {
51413                 id : uid
51414             },
51415             method: 'GET',
51416             success : function(req){
51417                //console.log(data);
51418                 var rdata = false;
51419                 var edata;
51420                 try  {
51421                    rdata = Roo.decode(req.responseText)
51422                 } catch (e) {
51423                     Roo.log("Invalid data from server..");
51424                     Roo.log(edata);
51425                     return;
51426                 }
51427                 if (!rdata || !rdata.success) {
51428                     Roo.log(rdata);
51429                     Roo.MessageBox.alert(Roo.encode(rdata));
51430                     return;
51431                 }
51432                 var data = rdata.data;
51433                 
51434                 if (this.uploadComplete) {
51435                    Roo.MessageBox.hide();
51436                    return;
51437                 }
51438                    
51439                 if (data){
51440                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51441                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51442                     );
51443                 }
51444                 this.uploadProgress.defer(2000,this);
51445             },
51446        
51447             failure: function(data) {
51448                 Roo.log('progress url failed ');
51449                 Roo.log(data);
51450             },
51451             scope : this
51452         });
51453            
51454     },
51455     
51456     
51457     run : function()
51458     {
51459         // run get Values on the form, so it syncs any secondary forms.
51460         this.form.getValues();
51461         
51462         var o = this.options;
51463         var method = this.getMethod();
51464         var isPost = method == 'POST';
51465         if(o.clientValidation === false || this.form.isValid()){
51466             
51467             if (this.form.progressUrl) {
51468                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51469                     (new Date() * 1) + '' + Math.random());
51470                     
51471             } 
51472             
51473             
51474             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51475                 form:this.form.el.dom,
51476                 url:this.getUrl(!isPost),
51477                 method: method,
51478                 params:isPost ? this.getParams() : null,
51479                 isUpload: this.form.fileUpload,
51480                 formData : this.form.formData
51481             }));
51482             
51483             this.uploadProgress();
51484
51485         }else if (o.clientValidation !== false){ // client validation failed
51486             this.failureType = Roo.form.Action.CLIENT_INVALID;
51487             this.form.afterAction(this, false);
51488         }
51489     },
51490
51491     success : function(response)
51492     {
51493         this.uploadComplete= true;
51494         if (this.haveProgress) {
51495             Roo.MessageBox.hide();
51496         }
51497         
51498         
51499         var result = this.processResponse(response);
51500         if(result === true || result.success){
51501             this.form.afterAction(this, true);
51502             return;
51503         }
51504         if(result.errors){
51505             this.form.markInvalid(result.errors);
51506             this.failureType = Roo.form.Action.SERVER_INVALID;
51507         }
51508         this.form.afterAction(this, false);
51509     },
51510     failure : function(response)
51511     {
51512         this.uploadComplete= true;
51513         if (this.haveProgress) {
51514             Roo.MessageBox.hide();
51515         }
51516         
51517         this.response = response;
51518         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51519         this.form.afterAction(this, false);
51520     },
51521     
51522     handleResponse : function(response){
51523         if(this.form.errorReader){
51524             var rs = this.form.errorReader.read(response);
51525             var errors = [];
51526             if(rs.records){
51527                 for(var i = 0, len = rs.records.length; i < len; i++) {
51528                     var r = rs.records[i];
51529                     errors[i] = r.data;
51530                 }
51531             }
51532             if(errors.length < 1){
51533                 errors = null;
51534             }
51535             return {
51536                 success : rs.success,
51537                 errors : errors
51538             };
51539         }
51540         var ret = false;
51541         try {
51542             ret = Roo.decode(response.responseText);
51543         } catch (e) {
51544             ret = {
51545                 success: false,
51546                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51547                 errors : []
51548             };
51549         }
51550         return ret;
51551         
51552     }
51553 });
51554
51555
51556 Roo.form.Action.Load = function(form, options){
51557     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51558     this.reader = this.form.reader;
51559 };
51560
51561 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51562     type : 'load',
51563
51564     run : function(){
51565         
51566         Roo.Ajax.request(Roo.apply(
51567                 this.createCallback(), {
51568                     method:this.getMethod(),
51569                     url:this.getUrl(false),
51570                     params:this.getParams()
51571         }));
51572     },
51573
51574     success : function(response){
51575         
51576         var result = this.processResponse(response);
51577         if(result === true || !result.success || !result.data){
51578             this.failureType = Roo.form.Action.LOAD_FAILURE;
51579             this.form.afterAction(this, false);
51580             return;
51581         }
51582         this.form.clearInvalid();
51583         this.form.setValues(result.data);
51584         this.form.afterAction(this, true);
51585     },
51586
51587     handleResponse : function(response){
51588         if(this.form.reader){
51589             var rs = this.form.reader.read(response);
51590             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51591             return {
51592                 success : rs.success,
51593                 data : data
51594             };
51595         }
51596         return Roo.decode(response.responseText);
51597     }
51598 });
51599
51600 Roo.form.Action.ACTION_TYPES = {
51601     'load' : Roo.form.Action.Load,
51602     'submit' : Roo.form.Action.Submit
51603 };/*
51604  * Based on:
51605  * Ext JS Library 1.1.1
51606  * Copyright(c) 2006-2007, Ext JS, LLC.
51607  *
51608  * Originally Released Under LGPL - original licence link has changed is not relivant.
51609  *
51610  * Fork - LGPL
51611  * <script type="text/javascript">
51612  */
51613  
51614 /**
51615  * @class Roo.form.Layout
51616  * @extends Roo.Component
51617  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51618  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51619  * @constructor
51620  * @param {Object} config Configuration options
51621  */
51622 Roo.form.Layout = function(config){
51623     var xitems = [];
51624     if (config.items) {
51625         xitems = config.items;
51626         delete config.items;
51627     }
51628     Roo.form.Layout.superclass.constructor.call(this, config);
51629     this.stack = [];
51630     Roo.each(xitems, this.addxtype, this);
51631      
51632 };
51633
51634 Roo.extend(Roo.form.Layout, Roo.Component, {
51635     /**
51636      * @cfg {String/Object} autoCreate
51637      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51638      */
51639     /**
51640      * @cfg {String/Object/Function} style
51641      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51642      * a function which returns such a specification.
51643      */
51644     /**
51645      * @cfg {String} labelAlign
51646      * Valid values are "left," "top" and "right" (defaults to "left")
51647      */
51648     /**
51649      * @cfg {Number} labelWidth
51650      * Fixed width in pixels of all field labels (defaults to undefined)
51651      */
51652     /**
51653      * @cfg {Boolean} clear
51654      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51655      */
51656     clear : true,
51657     /**
51658      * @cfg {String} labelSeparator
51659      * The separator to use after field labels (defaults to ':')
51660      */
51661     labelSeparator : ':',
51662     /**
51663      * @cfg {Boolean} hideLabels
51664      * True to suppress the display of field labels in this layout (defaults to false)
51665      */
51666     hideLabels : false,
51667
51668     // private
51669     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51670     
51671     isLayout : true,
51672     
51673     // private
51674     onRender : function(ct, position){
51675         if(this.el){ // from markup
51676             this.el = Roo.get(this.el);
51677         }else {  // generate
51678             var cfg = this.getAutoCreate();
51679             this.el = ct.createChild(cfg, position);
51680         }
51681         if(this.style){
51682             this.el.applyStyles(this.style);
51683         }
51684         if(this.labelAlign){
51685             this.el.addClass('x-form-label-'+this.labelAlign);
51686         }
51687         if(this.hideLabels){
51688             this.labelStyle = "display:none";
51689             this.elementStyle = "padding-left:0;";
51690         }else{
51691             if(typeof this.labelWidth == 'number'){
51692                 this.labelStyle = "width:"+this.labelWidth+"px;";
51693                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51694             }
51695             if(this.labelAlign == 'top'){
51696                 this.labelStyle = "width:auto;";
51697                 this.elementStyle = "padding-left:0;";
51698             }
51699         }
51700         var stack = this.stack;
51701         var slen = stack.length;
51702         if(slen > 0){
51703             if(!this.fieldTpl){
51704                 var t = new Roo.Template(
51705                     '<div class="x-form-item {5}">',
51706                         '<label for="{0}" style="{2}">{1}{4}</label>',
51707                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51708                         '</div>',
51709                     '</div><div class="x-form-clear-left"></div>'
51710                 );
51711                 t.disableFormats = true;
51712                 t.compile();
51713                 Roo.form.Layout.prototype.fieldTpl = t;
51714             }
51715             for(var i = 0; i < slen; i++) {
51716                 if(stack[i].isFormField){
51717                     this.renderField(stack[i]);
51718                 }else{
51719                     this.renderComponent(stack[i]);
51720                 }
51721             }
51722         }
51723         if(this.clear){
51724             this.el.createChild({cls:'x-form-clear'});
51725         }
51726     },
51727
51728     // private
51729     renderField : function(f){
51730         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51731                f.id, //0
51732                f.fieldLabel, //1
51733                f.labelStyle||this.labelStyle||'', //2
51734                this.elementStyle||'', //3
51735                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51736                f.itemCls||this.itemCls||''  //5
51737        ], true).getPrevSibling());
51738     },
51739
51740     // private
51741     renderComponent : function(c){
51742         c.render(c.isLayout ? this.el : this.el.createChild());    
51743     },
51744     /**
51745      * Adds a object form elements (using the xtype property as the factory method.)
51746      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51747      * @param {Object} config 
51748      */
51749     addxtype : function(o)
51750     {
51751         // create the lement.
51752         o.form = this.form;
51753         var fe = Roo.factory(o, Roo.form);
51754         this.form.allItems.push(fe);
51755         this.stack.push(fe);
51756         
51757         if (fe.isFormField) {
51758             this.form.items.add(fe);
51759         }
51760          
51761         return fe;
51762     }
51763 });
51764
51765 /**
51766  * @class Roo.form.Column
51767  * @extends Roo.form.Layout
51768  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51769  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51770  * @constructor
51771  * @param {Object} config Configuration options
51772  */
51773 Roo.form.Column = function(config){
51774     Roo.form.Column.superclass.constructor.call(this, config);
51775 };
51776
51777 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51778     /**
51779      * @cfg {Number/String} width
51780      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51781      */
51782     /**
51783      * @cfg {String/Object} autoCreate
51784      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51785      */
51786
51787     // private
51788     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51789
51790     // private
51791     onRender : function(ct, position){
51792         Roo.form.Column.superclass.onRender.call(this, ct, position);
51793         if(this.width){
51794             this.el.setWidth(this.width);
51795         }
51796     }
51797 });
51798
51799
51800 /**
51801  * @class Roo.form.Row
51802  * @extends Roo.form.Layout
51803  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51804  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51805  * @constructor
51806  * @param {Object} config Configuration options
51807  */
51808
51809  
51810 Roo.form.Row = function(config){
51811     Roo.form.Row.superclass.constructor.call(this, config);
51812 };
51813  
51814 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51815       /**
51816      * @cfg {Number/String} width
51817      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51818      */
51819     /**
51820      * @cfg {Number/String} height
51821      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51822      */
51823     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51824     
51825     padWidth : 20,
51826     // private
51827     onRender : function(ct, position){
51828         //console.log('row render');
51829         if(!this.rowTpl){
51830             var t = new Roo.Template(
51831                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51832                     '<label for="{0}" style="{2}">{1}{4}</label>',
51833                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51834                     '</div>',
51835                 '</div>'
51836             );
51837             t.disableFormats = true;
51838             t.compile();
51839             Roo.form.Layout.prototype.rowTpl = t;
51840         }
51841         this.fieldTpl = this.rowTpl;
51842         
51843         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51844         var labelWidth = 100;
51845         
51846         if ((this.labelAlign != 'top')) {
51847             if (typeof this.labelWidth == 'number') {
51848                 labelWidth = this.labelWidth
51849             }
51850             this.padWidth =  20 + labelWidth;
51851             
51852         }
51853         
51854         Roo.form.Column.superclass.onRender.call(this, ct, position);
51855         if(this.width){
51856             this.el.setWidth(this.width);
51857         }
51858         if(this.height){
51859             this.el.setHeight(this.height);
51860         }
51861     },
51862     
51863     // private
51864     renderField : function(f){
51865         f.fieldEl = this.fieldTpl.append(this.el, [
51866                f.id, f.fieldLabel,
51867                f.labelStyle||this.labelStyle||'',
51868                this.elementStyle||'',
51869                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51870                f.itemCls||this.itemCls||'',
51871                f.width ? f.width + this.padWidth : 160 + this.padWidth
51872        ],true);
51873     }
51874 });
51875  
51876
51877 /**
51878  * @class Roo.form.FieldSet
51879  * @extends Roo.form.Layout
51880  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51881  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51882  * @constructor
51883  * @param {Object} config Configuration options
51884  */
51885 Roo.form.FieldSet = function(config){
51886     Roo.form.FieldSet.superclass.constructor.call(this, config);
51887 };
51888
51889 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51890     /**
51891      * @cfg {String} legend
51892      * The text to display as the legend for the FieldSet (defaults to '')
51893      */
51894     /**
51895      * @cfg {String/Object} autoCreate
51896      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51897      */
51898
51899     // private
51900     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51901
51902     // private
51903     onRender : function(ct, position){
51904         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51905         if(this.legend){
51906             this.setLegend(this.legend);
51907         }
51908     },
51909
51910     // private
51911     setLegend : function(text){
51912         if(this.rendered){
51913             this.el.child('legend').update(text);
51914         }
51915     }
51916 });/*
51917  * Based on:
51918  * Ext JS Library 1.1.1
51919  * Copyright(c) 2006-2007, Ext JS, LLC.
51920  *
51921  * Originally Released Under LGPL - original licence link has changed is not relivant.
51922  *
51923  * Fork - LGPL
51924  * <script type="text/javascript">
51925  */
51926 /**
51927  * @class Roo.form.VTypes
51928  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51929  * @static
51930  */
51931 Roo.form.VTypes = function(){
51932     // closure these in so they are only created once.
51933     var alpha = /^[a-zA-Z_]+$/;
51934     var alphanum = /^[a-zA-Z0-9_]+$/;
51935     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51936     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51937
51938     // All these messages and functions are configurable
51939     return {
51940         /**
51941          * The function used to validate email addresses
51942          * @param {String} value The email address
51943          */
51944         'email' : function(v){
51945             return email.test(v);
51946         },
51947         /**
51948          * The error text to display when the email validation function returns false
51949          * @type String
51950          */
51951         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51952         /**
51953          * The keystroke filter mask to be applied on email input
51954          * @type RegExp
51955          */
51956         'emailMask' : /[a-z0-9_\.\-@]/i,
51957
51958         /**
51959          * The function used to validate URLs
51960          * @param {String} value The URL
51961          */
51962         'url' : function(v){
51963             return url.test(v);
51964         },
51965         /**
51966          * The error text to display when the url validation function returns false
51967          * @type String
51968          */
51969         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51970         
51971         /**
51972          * The function used to validate alpha values
51973          * @param {String} value The value
51974          */
51975         'alpha' : function(v){
51976             return alpha.test(v);
51977         },
51978         /**
51979          * The error text to display when the alpha validation function returns false
51980          * @type String
51981          */
51982         'alphaText' : 'This field should only contain letters and _',
51983         /**
51984          * The keystroke filter mask to be applied on alpha input
51985          * @type RegExp
51986          */
51987         'alphaMask' : /[a-z_]/i,
51988
51989         /**
51990          * The function used to validate alphanumeric values
51991          * @param {String} value The value
51992          */
51993         'alphanum' : function(v){
51994             return alphanum.test(v);
51995         },
51996         /**
51997          * The error text to display when the alphanumeric validation function returns false
51998          * @type String
51999          */
52000         'alphanumText' : 'This field should only contain letters, numbers and _',
52001         /**
52002          * The keystroke filter mask to be applied on alphanumeric input
52003          * @type RegExp
52004          */
52005         'alphanumMask' : /[a-z0-9_]/i
52006     };
52007 }();//<script type="text/javascript">
52008
52009 /**
52010  * @class Roo.form.FCKeditor
52011  * @extends Roo.form.TextArea
52012  * Wrapper around the FCKEditor http://www.fckeditor.net
52013  * @constructor
52014  * Creates a new FCKeditor
52015  * @param {Object} config Configuration options
52016  */
52017 Roo.form.FCKeditor = function(config){
52018     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52019     this.addEvents({
52020          /**
52021          * @event editorinit
52022          * Fired when the editor is initialized - you can add extra handlers here..
52023          * @param {FCKeditor} this
52024          * @param {Object} the FCK object.
52025          */
52026         editorinit : true
52027     });
52028     
52029     
52030 };
52031 Roo.form.FCKeditor.editors = { };
52032 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52033 {
52034     //defaultAutoCreate : {
52035     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52036     //},
52037     // private
52038     /**
52039      * @cfg {Object} fck options - see fck manual for details.
52040      */
52041     fckconfig : false,
52042     
52043     /**
52044      * @cfg {Object} fck toolbar set (Basic or Default)
52045      */
52046     toolbarSet : 'Basic',
52047     /**
52048      * @cfg {Object} fck BasePath
52049      */ 
52050     basePath : '/fckeditor/',
52051     
52052     
52053     frame : false,
52054     
52055     value : '',
52056     
52057    
52058     onRender : function(ct, position)
52059     {
52060         if(!this.el){
52061             this.defaultAutoCreate = {
52062                 tag: "textarea",
52063                 style:"width:300px;height:60px;",
52064                 autocomplete: "new-password"
52065             };
52066         }
52067         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52068         /*
52069         if(this.grow){
52070             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52071             if(this.preventScrollbars){
52072                 this.el.setStyle("overflow", "hidden");
52073             }
52074             this.el.setHeight(this.growMin);
52075         }
52076         */
52077         //console.log('onrender' + this.getId() );
52078         Roo.form.FCKeditor.editors[this.getId()] = this;
52079          
52080
52081         this.replaceTextarea() ;
52082         
52083     },
52084     
52085     getEditor : function() {
52086         return this.fckEditor;
52087     },
52088     /**
52089      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52090      * @param {Mixed} value The value to set
52091      */
52092     
52093     
52094     setValue : function(value)
52095     {
52096         //console.log('setValue: ' + value);
52097         
52098         if(typeof(value) == 'undefined') { // not sure why this is happending...
52099             return;
52100         }
52101         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52102         
52103         //if(!this.el || !this.getEditor()) {
52104         //    this.value = value;
52105             //this.setValue.defer(100,this,[value]);    
52106         //    return;
52107         //} 
52108         
52109         if(!this.getEditor()) {
52110             return;
52111         }
52112         
52113         this.getEditor().SetData(value);
52114         
52115         //
52116
52117     },
52118
52119     /**
52120      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52121      * @return {Mixed} value The field value
52122      */
52123     getValue : function()
52124     {
52125         
52126         if (this.frame && this.frame.dom.style.display == 'none') {
52127             return Roo.form.FCKeditor.superclass.getValue.call(this);
52128         }
52129         
52130         if(!this.el || !this.getEditor()) {
52131            
52132            // this.getValue.defer(100,this); 
52133             return this.value;
52134         }
52135        
52136         
52137         var value=this.getEditor().GetData();
52138         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52139         return Roo.form.FCKeditor.superclass.getValue.call(this);
52140         
52141
52142     },
52143
52144     /**
52145      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52146      * @return {Mixed} value The field value
52147      */
52148     getRawValue : function()
52149     {
52150         if (this.frame && this.frame.dom.style.display == 'none') {
52151             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52152         }
52153         
52154         if(!this.el || !this.getEditor()) {
52155             //this.getRawValue.defer(100,this); 
52156             return this.value;
52157             return;
52158         }
52159         
52160         
52161         
52162         var value=this.getEditor().GetData();
52163         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52164         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52165          
52166     },
52167     
52168     setSize : function(w,h) {
52169         
52170         
52171         
52172         //if (this.frame && this.frame.dom.style.display == 'none') {
52173         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52174         //    return;
52175         //}
52176         //if(!this.el || !this.getEditor()) {
52177         //    this.setSize.defer(100,this, [w,h]); 
52178         //    return;
52179         //}
52180         
52181         
52182         
52183         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52184         
52185         this.frame.dom.setAttribute('width', w);
52186         this.frame.dom.setAttribute('height', h);
52187         this.frame.setSize(w,h);
52188         
52189     },
52190     
52191     toggleSourceEdit : function(value) {
52192         
52193       
52194          
52195         this.el.dom.style.display = value ? '' : 'none';
52196         this.frame.dom.style.display = value ?  'none' : '';
52197         
52198     },
52199     
52200     
52201     focus: function(tag)
52202     {
52203         if (this.frame.dom.style.display == 'none') {
52204             return Roo.form.FCKeditor.superclass.focus.call(this);
52205         }
52206         if(!this.el || !this.getEditor()) {
52207             this.focus.defer(100,this, [tag]); 
52208             return;
52209         }
52210         
52211         
52212         
52213         
52214         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52215         this.getEditor().Focus();
52216         if (tgs.length) {
52217             if (!this.getEditor().Selection.GetSelection()) {
52218                 this.focus.defer(100,this, [tag]); 
52219                 return;
52220             }
52221             
52222             
52223             var r = this.getEditor().EditorDocument.createRange();
52224             r.setStart(tgs[0],0);
52225             r.setEnd(tgs[0],0);
52226             this.getEditor().Selection.GetSelection().removeAllRanges();
52227             this.getEditor().Selection.GetSelection().addRange(r);
52228             this.getEditor().Focus();
52229         }
52230         
52231     },
52232     
52233     
52234     
52235     replaceTextarea : function()
52236     {
52237         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52238             return ;
52239         }
52240         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52241         //{
52242             // We must check the elements firstly using the Id and then the name.
52243         var oTextarea = document.getElementById( this.getId() );
52244         
52245         var colElementsByName = document.getElementsByName( this.getId() ) ;
52246          
52247         oTextarea.style.display = 'none' ;
52248
52249         if ( oTextarea.tabIndex ) {            
52250             this.TabIndex = oTextarea.tabIndex ;
52251         }
52252         
52253         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52254         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52255         this.frame = Roo.get(this.getId() + '___Frame')
52256     },
52257     
52258     _getConfigHtml : function()
52259     {
52260         var sConfig = '' ;
52261
52262         for ( var o in this.fckconfig ) {
52263             sConfig += sConfig.length > 0  ? '&amp;' : '';
52264             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52265         }
52266
52267         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52268     },
52269     
52270     
52271     _getIFrameHtml : function()
52272     {
52273         var sFile = 'fckeditor.html' ;
52274         /* no idea what this is about..
52275         try
52276         {
52277             if ( (/fcksource=true/i).test( window.top.location.search ) )
52278                 sFile = 'fckeditor.original.html' ;
52279         }
52280         catch (e) { 
52281         */
52282
52283         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52284         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52285         
52286         
52287         var html = '<iframe id="' + this.getId() +
52288             '___Frame" src="' + sLink +
52289             '" width="' + this.width +
52290             '" height="' + this.height + '"' +
52291             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52292             ' frameborder="0" scrolling="no"></iframe>' ;
52293
52294         return html ;
52295     },
52296     
52297     _insertHtmlBefore : function( html, element )
52298     {
52299         if ( element.insertAdjacentHTML )       {
52300             // IE
52301             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52302         } else { // Gecko
52303             var oRange = document.createRange() ;
52304             oRange.setStartBefore( element ) ;
52305             var oFragment = oRange.createContextualFragment( html );
52306             element.parentNode.insertBefore( oFragment, element ) ;
52307         }
52308     }
52309     
52310     
52311   
52312     
52313     
52314     
52315     
52316
52317 });
52318
52319 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52320
52321 function FCKeditor_OnComplete(editorInstance){
52322     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52323     f.fckEditor = editorInstance;
52324     //console.log("loaded");
52325     f.fireEvent('editorinit', f, editorInstance);
52326
52327   
52328
52329  
52330
52331
52332
52333
52334
52335
52336
52337
52338
52339
52340
52341
52342
52343
52344
52345 //<script type="text/javascript">
52346 /**
52347  * @class Roo.form.GridField
52348  * @extends Roo.form.Field
52349  * Embed a grid (or editable grid into a form)
52350  * STATUS ALPHA
52351  * 
52352  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52353  * it needs 
52354  * xgrid.store = Roo.data.Store
52355  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52356  * xgrid.store.reader = Roo.data.JsonReader 
52357  * 
52358  * 
52359  * @constructor
52360  * Creates a new GridField
52361  * @param {Object} config Configuration options
52362  */
52363 Roo.form.GridField = function(config){
52364     Roo.form.GridField.superclass.constructor.call(this, config);
52365      
52366 };
52367
52368 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52369     /**
52370      * @cfg {Number} width  - used to restrict width of grid..
52371      */
52372     width : 100,
52373     /**
52374      * @cfg {Number} height - used to restrict height of grid..
52375      */
52376     height : 50,
52377      /**
52378      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52379          * 
52380          *}
52381      */
52382     xgrid : false, 
52383     /**
52384      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52385      * {tag: "input", type: "checkbox", autocomplete: "off"})
52386      */
52387    // defaultAutoCreate : { tag: 'div' },
52388     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52389     /**
52390      * @cfg {String} addTitle Text to include for adding a title.
52391      */
52392     addTitle : false,
52393     //
52394     onResize : function(){
52395         Roo.form.Field.superclass.onResize.apply(this, arguments);
52396     },
52397
52398     initEvents : function(){
52399         // Roo.form.Checkbox.superclass.initEvents.call(this);
52400         // has no events...
52401        
52402     },
52403
52404
52405     getResizeEl : function(){
52406         return this.wrap;
52407     },
52408
52409     getPositionEl : function(){
52410         return this.wrap;
52411     },
52412
52413     // private
52414     onRender : function(ct, position){
52415         
52416         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52417         var style = this.style;
52418         delete this.style;
52419         
52420         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52421         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52422         this.viewEl = this.wrap.createChild({ tag: 'div' });
52423         if (style) {
52424             this.viewEl.applyStyles(style);
52425         }
52426         if (this.width) {
52427             this.viewEl.setWidth(this.width);
52428         }
52429         if (this.height) {
52430             this.viewEl.setHeight(this.height);
52431         }
52432         //if(this.inputValue !== undefined){
52433         //this.setValue(this.value);
52434         
52435         
52436         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52437         
52438         
52439         this.grid.render();
52440         this.grid.getDataSource().on('remove', this.refreshValue, this);
52441         this.grid.getDataSource().on('update', this.refreshValue, this);
52442         this.grid.on('afteredit', this.refreshValue, this);
52443  
52444     },
52445      
52446     
52447     /**
52448      * Sets the value of the item. 
52449      * @param {String} either an object  or a string..
52450      */
52451     setValue : function(v){
52452         //this.value = v;
52453         v = v || []; // empty set..
52454         // this does not seem smart - it really only affects memoryproxy grids..
52455         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52456             var ds = this.grid.getDataSource();
52457             // assumes a json reader..
52458             var data = {}
52459             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52460             ds.loadData( data);
52461         }
52462         // clear selection so it does not get stale.
52463         if (this.grid.sm) { 
52464             this.grid.sm.clearSelections();
52465         }
52466         
52467         Roo.form.GridField.superclass.setValue.call(this, v);
52468         this.refreshValue();
52469         // should load data in the grid really....
52470     },
52471     
52472     // private
52473     refreshValue: function() {
52474          var val = [];
52475         this.grid.getDataSource().each(function(r) {
52476             val.push(r.data);
52477         });
52478         this.el.dom.value = Roo.encode(val);
52479     }
52480     
52481      
52482     
52483     
52484 });/*
52485  * Based on:
52486  * Ext JS Library 1.1.1
52487  * Copyright(c) 2006-2007, Ext JS, LLC.
52488  *
52489  * Originally Released Under LGPL - original licence link has changed is not relivant.
52490  *
52491  * Fork - LGPL
52492  * <script type="text/javascript">
52493  */
52494 /**
52495  * @class Roo.form.DisplayField
52496  * @extends Roo.form.Field
52497  * A generic Field to display non-editable data.
52498  * @cfg {Boolean} closable (true|false) default false
52499  * @constructor
52500  * Creates a new Display Field item.
52501  * @param {Object} config Configuration options
52502  */
52503 Roo.form.DisplayField = function(config){
52504     Roo.form.DisplayField.superclass.constructor.call(this, config);
52505     
52506     this.addEvents({
52507         /**
52508          * @event close
52509          * Fires after the click the close btn
52510              * @param {Roo.form.DisplayField} this
52511              */
52512         close : true
52513     });
52514 };
52515
52516 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52517     inputType:      'hidden',
52518     allowBlank:     true,
52519     readOnly:         true,
52520     
52521  
52522     /**
52523      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52524      */
52525     focusClass : undefined,
52526     /**
52527      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52528      */
52529     fieldClass: 'x-form-field',
52530     
52531      /**
52532      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52533      */
52534     valueRenderer: undefined,
52535     
52536     width: 100,
52537     /**
52538      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52539      * {tag: "input", type: "checkbox", autocomplete: "off"})
52540      */
52541      
52542  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52543  
52544     closable : false,
52545     
52546     onResize : function(){
52547         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52548         
52549     },
52550
52551     initEvents : function(){
52552         // Roo.form.Checkbox.superclass.initEvents.call(this);
52553         // has no events...
52554         
52555         if(this.closable){
52556             this.closeEl.on('click', this.onClose, this);
52557         }
52558        
52559     },
52560
52561
52562     getResizeEl : function(){
52563         return this.wrap;
52564     },
52565
52566     getPositionEl : function(){
52567         return this.wrap;
52568     },
52569
52570     // private
52571     onRender : function(ct, position){
52572         
52573         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52574         //if(this.inputValue !== undefined){
52575         this.wrap = this.el.wrap();
52576         
52577         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52578         
52579         if(this.closable){
52580             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52581         }
52582         
52583         if (this.bodyStyle) {
52584             this.viewEl.applyStyles(this.bodyStyle);
52585         }
52586         //this.viewEl.setStyle('padding', '2px');
52587         
52588         this.setValue(this.value);
52589         
52590     },
52591 /*
52592     // private
52593     initValue : Roo.emptyFn,
52594
52595   */
52596
52597         // private
52598     onClick : function(){
52599         
52600     },
52601
52602     /**
52603      * Sets the checked state of the checkbox.
52604      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52605      */
52606     setValue : function(v){
52607         this.value = v;
52608         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52609         // this might be called before we have a dom element..
52610         if (!this.viewEl) {
52611             return;
52612         }
52613         this.viewEl.dom.innerHTML = html;
52614         Roo.form.DisplayField.superclass.setValue.call(this, v);
52615
52616     },
52617     
52618     onClose : function(e)
52619     {
52620         e.preventDefault();
52621         
52622         this.fireEvent('close', this);
52623     }
52624 });/*
52625  * 
52626  * Licence- LGPL
52627  * 
52628  */
52629
52630 /**
52631  * @class Roo.form.DayPicker
52632  * @extends Roo.form.Field
52633  * A Day picker show [M] [T] [W] ....
52634  * @constructor
52635  * Creates a new Day Picker
52636  * @param {Object} config Configuration options
52637  */
52638 Roo.form.DayPicker= function(config){
52639     Roo.form.DayPicker.superclass.constructor.call(this, config);
52640      
52641 };
52642
52643 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52644     /**
52645      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52646      */
52647     focusClass : undefined,
52648     /**
52649      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52650      */
52651     fieldClass: "x-form-field",
52652    
52653     /**
52654      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52655      * {tag: "input", type: "checkbox", autocomplete: "off"})
52656      */
52657     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52658     
52659    
52660     actionMode : 'viewEl', 
52661     //
52662     // private
52663  
52664     inputType : 'hidden',
52665     
52666      
52667     inputElement: false, // real input element?
52668     basedOn: false, // ????
52669     
52670     isFormField: true, // not sure where this is needed!!!!
52671
52672     onResize : function(){
52673         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52674         if(!this.boxLabel){
52675             this.el.alignTo(this.wrap, 'c-c');
52676         }
52677     },
52678
52679     initEvents : function(){
52680         Roo.form.Checkbox.superclass.initEvents.call(this);
52681         this.el.on("click", this.onClick,  this);
52682         this.el.on("change", this.onClick,  this);
52683     },
52684
52685
52686     getResizeEl : function(){
52687         return this.wrap;
52688     },
52689
52690     getPositionEl : function(){
52691         return this.wrap;
52692     },
52693
52694     
52695     // private
52696     onRender : function(ct, position){
52697         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52698        
52699         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52700         
52701         var r1 = '<table><tr>';
52702         var r2 = '<tr class="x-form-daypick-icons">';
52703         for (var i=0; i < 7; i++) {
52704             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52705             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52706         }
52707         
52708         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52709         viewEl.select('img').on('click', this.onClick, this);
52710         this.viewEl = viewEl;   
52711         
52712         
52713         // this will not work on Chrome!!!
52714         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52715         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52716         
52717         
52718           
52719
52720     },
52721
52722     // private
52723     initValue : Roo.emptyFn,
52724
52725     /**
52726      * Returns the checked state of the checkbox.
52727      * @return {Boolean} True if checked, else false
52728      */
52729     getValue : function(){
52730         return this.el.dom.value;
52731         
52732     },
52733
52734         // private
52735     onClick : function(e){ 
52736         //this.setChecked(!this.checked);
52737         Roo.get(e.target).toggleClass('x-menu-item-checked');
52738         this.refreshValue();
52739         //if(this.el.dom.checked != this.checked){
52740         //    this.setValue(this.el.dom.checked);
52741        // }
52742     },
52743     
52744     // private
52745     refreshValue : function()
52746     {
52747         var val = '';
52748         this.viewEl.select('img',true).each(function(e,i,n)  {
52749             val += e.is(".x-menu-item-checked") ? String(n) : '';
52750         });
52751         this.setValue(val, true);
52752     },
52753
52754     /**
52755      * Sets the checked state of the checkbox.
52756      * On is always based on a string comparison between inputValue and the param.
52757      * @param {Boolean/String} value - the value to set 
52758      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52759      */
52760     setValue : function(v,suppressEvent){
52761         if (!this.el.dom) {
52762             return;
52763         }
52764         var old = this.el.dom.value ;
52765         this.el.dom.value = v;
52766         if (suppressEvent) {
52767             return ;
52768         }
52769          
52770         // update display..
52771         this.viewEl.select('img',true).each(function(e,i,n)  {
52772             
52773             var on = e.is(".x-menu-item-checked");
52774             var newv = v.indexOf(String(n)) > -1;
52775             if (on != newv) {
52776                 e.toggleClass('x-menu-item-checked');
52777             }
52778             
52779         });
52780         
52781         
52782         this.fireEvent('change', this, v, old);
52783         
52784         
52785     },
52786    
52787     // handle setting of hidden value by some other method!!?!?
52788     setFromHidden: function()
52789     {
52790         if(!this.el){
52791             return;
52792         }
52793         //console.log("SET FROM HIDDEN");
52794         //alert('setFrom hidden');
52795         this.setValue(this.el.dom.value);
52796     },
52797     
52798     onDestroy : function()
52799     {
52800         if(this.viewEl){
52801             Roo.get(this.viewEl).remove();
52802         }
52803          
52804         Roo.form.DayPicker.superclass.onDestroy.call(this);
52805     }
52806
52807 });/*
52808  * RooJS Library 1.1.1
52809  * Copyright(c) 2008-2011  Alan Knowles
52810  *
52811  * License - LGPL
52812  */
52813  
52814
52815 /**
52816  * @class Roo.form.ComboCheck
52817  * @extends Roo.form.ComboBox
52818  * A combobox for multiple select items.
52819  *
52820  * FIXME - could do with a reset button..
52821  * 
52822  * @constructor
52823  * Create a new ComboCheck
52824  * @param {Object} config Configuration options
52825  */
52826 Roo.form.ComboCheck = function(config){
52827     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52828     // should verify some data...
52829     // like
52830     // hiddenName = required..
52831     // displayField = required
52832     // valudField == required
52833     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52834     var _t = this;
52835     Roo.each(req, function(e) {
52836         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52837             throw "Roo.form.ComboCheck : missing value for: " + e;
52838         }
52839     });
52840     
52841     
52842 };
52843
52844 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52845      
52846      
52847     editable : false,
52848      
52849     selectedClass: 'x-menu-item-checked', 
52850     
52851     // private
52852     onRender : function(ct, position){
52853         var _t = this;
52854         
52855         
52856         
52857         if(!this.tpl){
52858             var cls = 'x-combo-list';
52859
52860             
52861             this.tpl =  new Roo.Template({
52862                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52863                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52864                    '<span>{' + this.displayField + '}</span>' +
52865                     '</div>' 
52866                 
52867             });
52868         }
52869  
52870         
52871         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52872         this.view.singleSelect = false;
52873         this.view.multiSelect = true;
52874         this.view.toggleSelect = true;
52875         this.pageTb.add(new Roo.Toolbar.Fill(), {
52876             
52877             text: 'Done',
52878             handler: function()
52879             {
52880                 _t.collapse();
52881             }
52882         });
52883     },
52884     
52885     onViewOver : function(e, t){
52886         // do nothing...
52887         return;
52888         
52889     },
52890     
52891     onViewClick : function(doFocus,index){
52892         return;
52893         
52894     },
52895     select: function () {
52896         //Roo.log("SELECT CALLED");
52897     },
52898      
52899     selectByValue : function(xv, scrollIntoView){
52900         var ar = this.getValueArray();
52901         var sels = [];
52902         
52903         Roo.each(ar, function(v) {
52904             if(v === undefined || v === null){
52905                 return;
52906             }
52907             var r = this.findRecord(this.valueField, v);
52908             if(r){
52909                 sels.push(this.store.indexOf(r))
52910                 
52911             }
52912         },this);
52913         this.view.select(sels);
52914         return false;
52915     },
52916     
52917     
52918     
52919     onSelect : function(record, index){
52920        // Roo.log("onselect Called");
52921        // this is only called by the clear button now..
52922         this.view.clearSelections();
52923         this.setValue('[]');
52924         if (this.value != this.valueBefore) {
52925             this.fireEvent('change', this, this.value, this.valueBefore);
52926             this.valueBefore = this.value;
52927         }
52928     },
52929     getValueArray : function()
52930     {
52931         var ar = [] ;
52932         
52933         try {
52934             //Roo.log(this.value);
52935             if (typeof(this.value) == 'undefined') {
52936                 return [];
52937             }
52938             var ar = Roo.decode(this.value);
52939             return  ar instanceof Array ? ar : []; //?? valid?
52940             
52941         } catch(e) {
52942             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52943             return [];
52944         }
52945          
52946     },
52947     expand : function ()
52948     {
52949         
52950         Roo.form.ComboCheck.superclass.expand.call(this);
52951         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52952         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52953         
52954
52955     },
52956     
52957     collapse : function(){
52958         Roo.form.ComboCheck.superclass.collapse.call(this);
52959         var sl = this.view.getSelectedIndexes();
52960         var st = this.store;
52961         var nv = [];
52962         var tv = [];
52963         var r;
52964         Roo.each(sl, function(i) {
52965             r = st.getAt(i);
52966             nv.push(r.get(this.valueField));
52967         },this);
52968         this.setValue(Roo.encode(nv));
52969         if (this.value != this.valueBefore) {
52970
52971             this.fireEvent('change', this, this.value, this.valueBefore);
52972             this.valueBefore = this.value;
52973         }
52974         
52975     },
52976     
52977     setValue : function(v){
52978         // Roo.log(v);
52979         this.value = v;
52980         
52981         var vals = this.getValueArray();
52982         var tv = [];
52983         Roo.each(vals, function(k) {
52984             var r = this.findRecord(this.valueField, k);
52985             if(r){
52986                 tv.push(r.data[this.displayField]);
52987             }else if(this.valueNotFoundText !== undefined){
52988                 tv.push( this.valueNotFoundText );
52989             }
52990         },this);
52991        // Roo.log(tv);
52992         
52993         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
52994         this.hiddenField.value = v;
52995         this.value = v;
52996     }
52997     
52998 });/*
52999  * Based on:
53000  * Ext JS Library 1.1.1
53001  * Copyright(c) 2006-2007, Ext JS, LLC.
53002  *
53003  * Originally Released Under LGPL - original licence link has changed is not relivant.
53004  *
53005  * Fork - LGPL
53006  * <script type="text/javascript">
53007  */
53008  
53009 /**
53010  * @class Roo.form.Signature
53011  * @extends Roo.form.Field
53012  * Signature field.  
53013  * @constructor
53014  * 
53015  * @param {Object} config Configuration options
53016  */
53017
53018 Roo.form.Signature = function(config){
53019     Roo.form.Signature.superclass.constructor.call(this, config);
53020     
53021     this.addEvents({// not in used??
53022          /**
53023          * @event confirm
53024          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53025              * @param {Roo.form.Signature} combo This combo box
53026              */
53027         'confirm' : true,
53028         /**
53029          * @event reset
53030          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53031              * @param {Roo.form.ComboBox} combo This combo box
53032              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53033              */
53034         'reset' : true
53035     });
53036 };
53037
53038 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53039     /**
53040      * @cfg {Object} labels Label to use when rendering a form.
53041      * defaults to 
53042      * labels : { 
53043      *      clear : "Clear",
53044      *      confirm : "Confirm"
53045      *  }
53046      */
53047     labels : { 
53048         clear : "Clear",
53049         confirm : "Confirm"
53050     },
53051     /**
53052      * @cfg {Number} width The signature panel width (defaults to 300)
53053      */
53054     width: 300,
53055     /**
53056      * @cfg {Number} height The signature panel height (defaults to 100)
53057      */
53058     height : 100,
53059     /**
53060      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53061      */
53062     allowBlank : false,
53063     
53064     //private
53065     // {Object} signPanel The signature SVG panel element (defaults to {})
53066     signPanel : {},
53067     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53068     isMouseDown : false,
53069     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53070     isConfirmed : false,
53071     // {String} signatureTmp SVG mapping string (defaults to empty string)
53072     signatureTmp : '',
53073     
53074     
53075     defaultAutoCreate : { // modified by initCompnoent..
53076         tag: "input",
53077         type:"hidden"
53078     },
53079
53080     // private
53081     onRender : function(ct, position){
53082         
53083         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53084         
53085         this.wrap = this.el.wrap({
53086             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53087         });
53088         
53089         this.createToolbar(this);
53090         this.signPanel = this.wrap.createChild({
53091                 tag: 'div',
53092                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53093             }, this.el
53094         );
53095             
53096         this.svgID = Roo.id();
53097         this.svgEl = this.signPanel.createChild({
53098               xmlns : 'http://www.w3.org/2000/svg',
53099               tag : 'svg',
53100               id : this.svgID + "-svg",
53101               width: this.width,
53102               height: this.height,
53103               viewBox: '0 0 '+this.width+' '+this.height,
53104               cn : [
53105                 {
53106                     tag: "rect",
53107                     id: this.svgID + "-svg-r",
53108                     width: this.width,
53109                     height: this.height,
53110                     fill: "#ffa"
53111                 },
53112                 {
53113                     tag: "line",
53114                     id: this.svgID + "-svg-l",
53115                     x1: "0", // start
53116                     y1: (this.height*0.8), // start set the line in 80% of height
53117                     x2: this.width, // end
53118                     y2: (this.height*0.8), // end set the line in 80% of height
53119                     'stroke': "#666",
53120                     'stroke-width': "1",
53121                     'stroke-dasharray': "3",
53122                     'shape-rendering': "crispEdges",
53123                     'pointer-events': "none"
53124                 },
53125                 {
53126                     tag: "path",
53127                     id: this.svgID + "-svg-p",
53128                     'stroke': "navy",
53129                     'stroke-width': "3",
53130                     'fill': "none",
53131                     'pointer-events': 'none'
53132                 }
53133               ]
53134         });
53135         this.createSVG();
53136         this.svgBox = this.svgEl.dom.getScreenCTM();
53137     },
53138     createSVG : function(){ 
53139         var svg = this.signPanel;
53140         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53141         var t = this;
53142
53143         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53144         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53145         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53146         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53147         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53148         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53149         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53150         
53151     },
53152     isTouchEvent : function(e){
53153         return e.type.match(/^touch/);
53154     },
53155     getCoords : function (e) {
53156         var pt    = this.svgEl.dom.createSVGPoint();
53157         pt.x = e.clientX; 
53158         pt.y = e.clientY;
53159         if (this.isTouchEvent(e)) {
53160             pt.x =  e.targetTouches[0].clientX;
53161             pt.y = e.targetTouches[0].clientY;
53162         }
53163         var a = this.svgEl.dom.getScreenCTM();
53164         var b = a.inverse();
53165         var mx = pt.matrixTransform(b);
53166         return mx.x + ',' + mx.y;
53167     },
53168     //mouse event headler 
53169     down : function (e) {
53170         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53171         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53172         
53173         this.isMouseDown = true;
53174         
53175         e.preventDefault();
53176     },
53177     move : function (e) {
53178         if (this.isMouseDown) {
53179             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53180             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53181         }
53182         
53183         e.preventDefault();
53184     },
53185     up : function (e) {
53186         this.isMouseDown = false;
53187         var sp = this.signatureTmp.split(' ');
53188         
53189         if(sp.length > 1){
53190             if(!sp[sp.length-2].match(/^L/)){
53191                 sp.pop();
53192                 sp.pop();
53193                 sp.push("");
53194                 this.signatureTmp = sp.join(" ");
53195             }
53196         }
53197         if(this.getValue() != this.signatureTmp){
53198             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53199             this.isConfirmed = false;
53200         }
53201         e.preventDefault();
53202     },
53203     
53204     /**
53205      * Protected method that will not generally be called directly. It
53206      * is called when the editor creates its toolbar. Override this method if you need to
53207      * add custom toolbar buttons.
53208      * @param {HtmlEditor} editor
53209      */
53210     createToolbar : function(editor){
53211          function btn(id, toggle, handler){
53212             var xid = fid + '-'+ id ;
53213             return {
53214                 id : xid,
53215                 cmd : id,
53216                 cls : 'x-btn-icon x-edit-'+id,
53217                 enableToggle:toggle !== false,
53218                 scope: editor, // was editor...
53219                 handler:handler||editor.relayBtnCmd,
53220                 clickEvent:'mousedown',
53221                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53222                 tabIndex:-1
53223             };
53224         }
53225         
53226         
53227         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53228         this.tb = tb;
53229         this.tb.add(
53230            {
53231                 cls : ' x-signature-btn x-signature-'+id,
53232                 scope: editor, // was editor...
53233                 handler: this.reset,
53234                 clickEvent:'mousedown',
53235                 text: this.labels.clear
53236             },
53237             {
53238                  xtype : 'Fill',
53239                  xns: Roo.Toolbar
53240             }, 
53241             {
53242                 cls : '  x-signature-btn x-signature-'+id,
53243                 scope: editor, // was editor...
53244                 handler: this.confirmHandler,
53245                 clickEvent:'mousedown',
53246                 text: this.labels.confirm
53247             }
53248         );
53249     
53250     },
53251     //public
53252     /**
53253      * when user is clicked confirm then show this image.....
53254      * 
53255      * @return {String} Image Data URI
53256      */
53257     getImageDataURI : function(){
53258         var svg = this.svgEl.dom.parentNode.innerHTML;
53259         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53260         return src; 
53261     },
53262     /**
53263      * 
53264      * @return {Boolean} this.isConfirmed
53265      */
53266     getConfirmed : function(){
53267         return this.isConfirmed;
53268     },
53269     /**
53270      * 
53271      * @return {Number} this.width
53272      */
53273     getWidth : function(){
53274         return this.width;
53275     },
53276     /**
53277      * 
53278      * @return {Number} this.height
53279      */
53280     getHeight : function(){
53281         return this.height;
53282     },
53283     // private
53284     getSignature : function(){
53285         return this.signatureTmp;
53286     },
53287     // private
53288     reset : function(){
53289         this.signatureTmp = '';
53290         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53291         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53292         this.isConfirmed = false;
53293         Roo.form.Signature.superclass.reset.call(this);
53294     },
53295     setSignature : function(s){
53296         this.signatureTmp = s;
53297         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53298         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53299         this.setValue(s);
53300         this.isConfirmed = false;
53301         Roo.form.Signature.superclass.reset.call(this);
53302     }, 
53303     test : function(){
53304 //        Roo.log(this.signPanel.dom.contentWindow.up())
53305     },
53306     //private
53307     setConfirmed : function(){
53308         
53309         
53310         
53311 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53312     },
53313     // private
53314     confirmHandler : function(){
53315         if(!this.getSignature()){
53316             return;
53317         }
53318         
53319         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53320         this.setValue(this.getSignature());
53321         this.isConfirmed = true;
53322         
53323         this.fireEvent('confirm', this);
53324     },
53325     // private
53326     // Subclasses should provide the validation implementation by overriding this
53327     validateValue : function(value){
53328         if(this.allowBlank){
53329             return true;
53330         }
53331         
53332         if(this.isConfirmed){
53333             return true;
53334         }
53335         return false;
53336     }
53337 });/*
53338  * Based on:
53339  * Ext JS Library 1.1.1
53340  * Copyright(c) 2006-2007, Ext JS, LLC.
53341  *
53342  * Originally Released Under LGPL - original licence link has changed is not relivant.
53343  *
53344  * Fork - LGPL
53345  * <script type="text/javascript">
53346  */
53347  
53348
53349 /**
53350  * @class Roo.form.ComboBox
53351  * @extends Roo.form.TriggerField
53352  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53353  * @constructor
53354  * Create a new ComboBox.
53355  * @param {Object} config Configuration options
53356  */
53357 Roo.form.Select = function(config){
53358     Roo.form.Select.superclass.constructor.call(this, config);
53359      
53360 };
53361
53362 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53363     /**
53364      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53365      */
53366     /**
53367      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53368      * rendering into an Roo.Editor, defaults to false)
53369      */
53370     /**
53371      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53372      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53373      */
53374     /**
53375      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53376      */
53377     /**
53378      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53379      * the dropdown list (defaults to undefined, with no header element)
53380      */
53381
53382      /**
53383      * @cfg {String/Roo.Template} tpl The template to use to render the output
53384      */
53385      
53386     // private
53387     defaultAutoCreate : {tag: "select"  },
53388     /**
53389      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53390      */
53391     listWidth: undefined,
53392     /**
53393      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53394      * mode = 'remote' or 'text' if mode = 'local')
53395      */
53396     displayField: undefined,
53397     /**
53398      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53399      * mode = 'remote' or 'value' if mode = 'local'). 
53400      * Note: use of a valueField requires the user make a selection
53401      * in order for a value to be mapped.
53402      */
53403     valueField: undefined,
53404     
53405     
53406     /**
53407      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53408      * field's data value (defaults to the underlying DOM element's name)
53409      */
53410     hiddenName: undefined,
53411     /**
53412      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53413      */
53414     listClass: '',
53415     /**
53416      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53417      */
53418     selectedClass: 'x-combo-selected',
53419     /**
53420      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53421      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53422      * which displays a downward arrow icon).
53423      */
53424     triggerClass : 'x-form-arrow-trigger',
53425     /**
53426      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53427      */
53428     shadow:'sides',
53429     /**
53430      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53431      * anchor positions (defaults to 'tl-bl')
53432      */
53433     listAlign: 'tl-bl?',
53434     /**
53435      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53436      */
53437     maxHeight: 300,
53438     /**
53439      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53440      * query specified by the allQuery config option (defaults to 'query')
53441      */
53442     triggerAction: 'query',
53443     /**
53444      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53445      * (defaults to 4, does not apply if editable = false)
53446      */
53447     minChars : 4,
53448     /**
53449      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53450      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53451      */
53452     typeAhead: false,
53453     /**
53454      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53455      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53456      */
53457     queryDelay: 500,
53458     /**
53459      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53460      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53461      */
53462     pageSize: 0,
53463     /**
53464      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53465      * when editable = true (defaults to false)
53466      */
53467     selectOnFocus:false,
53468     /**
53469      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53470      */
53471     queryParam: 'query',
53472     /**
53473      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53474      * when mode = 'remote' (defaults to 'Loading...')
53475      */
53476     loadingText: 'Loading...',
53477     /**
53478      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53479      */
53480     resizable: false,
53481     /**
53482      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53483      */
53484     handleHeight : 8,
53485     /**
53486      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53487      * traditional select (defaults to true)
53488      */
53489     editable: true,
53490     /**
53491      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53492      */
53493     allQuery: '',
53494     /**
53495      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53496      */
53497     mode: 'remote',
53498     /**
53499      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53500      * listWidth has a higher value)
53501      */
53502     minListWidth : 70,
53503     /**
53504      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53505      * allow the user to set arbitrary text into the field (defaults to false)
53506      */
53507     forceSelection:false,
53508     /**
53509      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53510      * if typeAhead = true (defaults to 250)
53511      */
53512     typeAheadDelay : 250,
53513     /**
53514      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53515      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53516      */
53517     valueNotFoundText : undefined,
53518     
53519     /**
53520      * @cfg {String} defaultValue The value displayed after loading the store.
53521      */
53522     defaultValue: '',
53523     
53524     /**
53525      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53526      */
53527     blockFocus : false,
53528     
53529     /**
53530      * @cfg {Boolean} disableClear Disable showing of clear button.
53531      */
53532     disableClear : false,
53533     /**
53534      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53535      */
53536     alwaysQuery : false,
53537     
53538     //private
53539     addicon : false,
53540     editicon: false,
53541     
53542     // element that contains real text value.. (when hidden is used..)
53543      
53544     // private
53545     onRender : function(ct, position){
53546         Roo.form.Field.prototype.onRender.call(this, ct, position);
53547         
53548         if(this.store){
53549             this.store.on('beforeload', this.onBeforeLoad, this);
53550             this.store.on('load', this.onLoad, this);
53551             this.store.on('loadexception', this.onLoadException, this);
53552             this.store.load({});
53553         }
53554         
53555         
53556         
53557     },
53558
53559     // private
53560     initEvents : function(){
53561         //Roo.form.ComboBox.superclass.initEvents.call(this);
53562  
53563     },
53564
53565     onDestroy : function(){
53566        
53567         if(this.store){
53568             this.store.un('beforeload', this.onBeforeLoad, this);
53569             this.store.un('load', this.onLoad, this);
53570             this.store.un('loadexception', this.onLoadException, this);
53571         }
53572         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53573     },
53574
53575     // private
53576     fireKey : function(e){
53577         if(e.isNavKeyPress() && !this.list.isVisible()){
53578             this.fireEvent("specialkey", this, e);
53579         }
53580     },
53581
53582     // private
53583     onResize: function(w, h){
53584         
53585         return; 
53586     
53587         
53588     },
53589
53590     /**
53591      * Allow or prevent the user from directly editing the field text.  If false is passed,
53592      * the user will only be able to select from the items defined in the dropdown list.  This method
53593      * is the runtime equivalent of setting the 'editable' config option at config time.
53594      * @param {Boolean} value True to allow the user to directly edit the field text
53595      */
53596     setEditable : function(value){
53597          
53598     },
53599
53600     // private
53601     onBeforeLoad : function(){
53602         
53603         Roo.log("Select before load");
53604         return;
53605     
53606         this.innerList.update(this.loadingText ?
53607                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53608         //this.restrictHeight();
53609         this.selectedIndex = -1;
53610     },
53611
53612     // private
53613     onLoad : function(){
53614
53615     
53616         var dom = this.el.dom;
53617         dom.innerHTML = '';
53618          var od = dom.ownerDocument;
53619          
53620         if (this.emptyText) {
53621             var op = od.createElement('option');
53622             op.setAttribute('value', '');
53623             op.innerHTML = String.format('{0}', this.emptyText);
53624             dom.appendChild(op);
53625         }
53626         if(this.store.getCount() > 0){
53627            
53628             var vf = this.valueField;
53629             var df = this.displayField;
53630             this.store.data.each(function(r) {
53631                 // which colmsn to use... testing - cdoe / title..
53632                 var op = od.createElement('option');
53633                 op.setAttribute('value', r.data[vf]);
53634                 op.innerHTML = String.format('{0}', r.data[df]);
53635                 dom.appendChild(op);
53636             });
53637             if (typeof(this.defaultValue != 'undefined')) {
53638                 this.setValue(this.defaultValue);
53639             }
53640             
53641              
53642         }else{
53643             //this.onEmptyResults();
53644         }
53645         //this.el.focus();
53646     },
53647     // private
53648     onLoadException : function()
53649     {
53650         dom.innerHTML = '';
53651             
53652         Roo.log("Select on load exception");
53653         return;
53654     
53655         this.collapse();
53656         Roo.log(this.store.reader.jsonData);
53657         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53658             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53659         }
53660         
53661         
53662     },
53663     // private
53664     onTypeAhead : function(){
53665          
53666     },
53667
53668     // private
53669     onSelect : function(record, index){
53670         Roo.log('on select?');
53671         return;
53672         if(this.fireEvent('beforeselect', this, record, index) !== false){
53673             this.setFromData(index > -1 ? record.data : false);
53674             this.collapse();
53675             this.fireEvent('select', this, record, index);
53676         }
53677     },
53678
53679     /**
53680      * Returns the currently selected field value or empty string if no value is set.
53681      * @return {String} value The selected value
53682      */
53683     getValue : function(){
53684         var dom = this.el.dom;
53685         this.value = dom.options[dom.selectedIndex].value;
53686         return this.value;
53687         
53688     },
53689
53690     /**
53691      * Clears any text/value currently set in the field
53692      */
53693     clearValue : function(){
53694         this.value = '';
53695         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53696         
53697     },
53698
53699     /**
53700      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53701      * will be displayed in the field.  If the value does not match the data value of an existing item,
53702      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53703      * Otherwise the field will be blank (although the value will still be set).
53704      * @param {String} value The value to match
53705      */
53706     setValue : function(v){
53707         var d = this.el.dom;
53708         for (var i =0; i < d.options.length;i++) {
53709             if (v == d.options[i].value) {
53710                 d.selectedIndex = i;
53711                 this.value = v;
53712                 return;
53713             }
53714         }
53715         this.clearValue();
53716     },
53717     /**
53718      * @property {Object} the last set data for the element
53719      */
53720     
53721     lastData : false,
53722     /**
53723      * Sets the value of the field based on a object which is related to the record format for the store.
53724      * @param {Object} value the value to set as. or false on reset?
53725      */
53726     setFromData : function(o){
53727         Roo.log('setfrom data?');
53728          
53729         
53730         
53731     },
53732     // private
53733     reset : function(){
53734         this.clearValue();
53735     },
53736     // private
53737     findRecord : function(prop, value){
53738         
53739         return false;
53740     
53741         var record;
53742         if(this.store.getCount() > 0){
53743             this.store.each(function(r){
53744                 if(r.data[prop] == value){
53745                     record = r;
53746                     return false;
53747                 }
53748                 return true;
53749             });
53750         }
53751         return record;
53752     },
53753     
53754     getName: function()
53755     {
53756         // returns hidden if it's set..
53757         if (!this.rendered) {return ''};
53758         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53759         
53760     },
53761      
53762
53763     
53764
53765     // private
53766     onEmptyResults : function(){
53767         Roo.log('empty results');
53768         //this.collapse();
53769     },
53770
53771     /**
53772      * Returns true if the dropdown list is expanded, else false.
53773      */
53774     isExpanded : function(){
53775         return false;
53776     },
53777
53778     /**
53779      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53780      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53781      * @param {String} value The data value of the item to select
53782      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53783      * selected item if it is not currently in view (defaults to true)
53784      * @return {Boolean} True if the value matched an item in the list, else false
53785      */
53786     selectByValue : function(v, scrollIntoView){
53787         Roo.log('select By Value');
53788         return false;
53789     
53790         if(v !== undefined && v !== null){
53791             var r = this.findRecord(this.valueField || this.displayField, v);
53792             if(r){
53793                 this.select(this.store.indexOf(r), scrollIntoView);
53794                 return true;
53795             }
53796         }
53797         return false;
53798     },
53799
53800     /**
53801      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53802      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53803      * @param {Number} index The zero-based index of the list item to select
53804      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53805      * selected item if it is not currently in view (defaults to true)
53806      */
53807     select : function(index, scrollIntoView){
53808         Roo.log('select ');
53809         return  ;
53810         
53811         this.selectedIndex = index;
53812         this.view.select(index);
53813         if(scrollIntoView !== false){
53814             var el = this.view.getNode(index);
53815             if(el){
53816                 this.innerList.scrollChildIntoView(el, false);
53817             }
53818         }
53819     },
53820
53821       
53822
53823     // private
53824     validateBlur : function(){
53825         
53826         return;
53827         
53828     },
53829
53830     // private
53831     initQuery : function(){
53832         this.doQuery(this.getRawValue());
53833     },
53834
53835     // private
53836     doForce : function(){
53837         if(this.el.dom.value.length > 0){
53838             this.el.dom.value =
53839                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53840              
53841         }
53842     },
53843
53844     /**
53845      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53846      * query allowing the query action to be canceled if needed.
53847      * @param {String} query The SQL query to execute
53848      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53849      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53850      * saved in the current store (defaults to false)
53851      */
53852     doQuery : function(q, forceAll){
53853         
53854         Roo.log('doQuery?');
53855         if(q === undefined || q === null){
53856             q = '';
53857         }
53858         var qe = {
53859             query: q,
53860             forceAll: forceAll,
53861             combo: this,
53862             cancel:false
53863         };
53864         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53865             return false;
53866         }
53867         q = qe.query;
53868         forceAll = qe.forceAll;
53869         if(forceAll === true || (q.length >= this.minChars)){
53870             if(this.lastQuery != q || this.alwaysQuery){
53871                 this.lastQuery = q;
53872                 if(this.mode == 'local'){
53873                     this.selectedIndex = -1;
53874                     if(forceAll){
53875                         this.store.clearFilter();
53876                     }else{
53877                         this.store.filter(this.displayField, q);
53878                     }
53879                     this.onLoad();
53880                 }else{
53881                     this.store.baseParams[this.queryParam] = q;
53882                     this.store.load({
53883                         params: this.getParams(q)
53884                     });
53885                     this.expand();
53886                 }
53887             }else{
53888                 this.selectedIndex = -1;
53889                 this.onLoad();   
53890             }
53891         }
53892     },
53893
53894     // private
53895     getParams : function(q){
53896         var p = {};
53897         //p[this.queryParam] = q;
53898         if(this.pageSize){
53899             p.start = 0;
53900             p.limit = this.pageSize;
53901         }
53902         return p;
53903     },
53904
53905     /**
53906      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53907      */
53908     collapse : function(){
53909         
53910     },
53911
53912     // private
53913     collapseIf : function(e){
53914         
53915     },
53916
53917     /**
53918      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53919      */
53920     expand : function(){
53921         
53922     } ,
53923
53924     // private
53925      
53926
53927     /** 
53928     * @cfg {Boolean} grow 
53929     * @hide 
53930     */
53931     /** 
53932     * @cfg {Number} growMin 
53933     * @hide 
53934     */
53935     /** 
53936     * @cfg {Number} growMax 
53937     * @hide 
53938     */
53939     /**
53940      * @hide
53941      * @method autoSize
53942      */
53943     
53944     setWidth : function()
53945     {
53946         
53947     },
53948     getResizeEl : function(){
53949         return this.el;
53950     }
53951 });//<script type="text/javasscript">
53952  
53953
53954 /**
53955  * @class Roo.DDView
53956  * A DnD enabled version of Roo.View.
53957  * @param {Element/String} container The Element in which to create the View.
53958  * @param {String} tpl The template string used to create the markup for each element of the View
53959  * @param {Object} config The configuration properties. These include all the config options of
53960  * {@link Roo.View} plus some specific to this class.<br>
53961  * <p>
53962  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53963  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53964  * <p>
53965  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53966 .x-view-drag-insert-above {
53967         border-top:1px dotted #3366cc;
53968 }
53969 .x-view-drag-insert-below {
53970         border-bottom:1px dotted #3366cc;
53971 }
53972 </code></pre>
53973  * 
53974  */
53975  
53976 Roo.DDView = function(container, tpl, config) {
53977     Roo.DDView.superclass.constructor.apply(this, arguments);
53978     this.getEl().setStyle("outline", "0px none");
53979     this.getEl().unselectable();
53980     if (this.dragGroup) {
53981         this.setDraggable(this.dragGroup.split(","));
53982     }
53983     if (this.dropGroup) {
53984         this.setDroppable(this.dropGroup.split(","));
53985     }
53986     if (this.deletable) {
53987         this.setDeletable();
53988     }
53989     this.isDirtyFlag = false;
53990         this.addEvents({
53991                 "drop" : true
53992         });
53993 };
53994
53995 Roo.extend(Roo.DDView, Roo.View, {
53996 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
53997 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
53998 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
53999 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54000
54001         isFormField: true,
54002
54003         reset: Roo.emptyFn,
54004         
54005         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54006
54007         validate: function() {
54008                 return true;
54009         },
54010         
54011         destroy: function() {
54012                 this.purgeListeners();
54013                 this.getEl.removeAllListeners();
54014                 this.getEl().remove();
54015                 if (this.dragZone) {
54016                         if (this.dragZone.destroy) {
54017                                 this.dragZone.destroy();
54018                         }
54019                 }
54020                 if (this.dropZone) {
54021                         if (this.dropZone.destroy) {
54022                                 this.dropZone.destroy();
54023                         }
54024                 }
54025         },
54026
54027 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54028         getName: function() {
54029                 return this.name;
54030         },
54031
54032 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54033         setValue: function(v) {
54034                 if (!this.store) {
54035                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54036                 }
54037                 var data = {};
54038                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54039                 this.store.proxy = new Roo.data.MemoryProxy(data);
54040                 this.store.load();
54041         },
54042
54043 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54044         getValue: function() {
54045                 var result = '(';
54046                 this.store.each(function(rec) {
54047                         result += rec.id + ',';
54048                 });
54049                 return result.substr(0, result.length - 1) + ')';
54050         },
54051         
54052         getIds: function() {
54053                 var i = 0, result = new Array(this.store.getCount());
54054                 this.store.each(function(rec) {
54055                         result[i++] = rec.id;
54056                 });
54057                 return result;
54058         },
54059         
54060         isDirty: function() {
54061                 return this.isDirtyFlag;
54062         },
54063
54064 /**
54065  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54066  *      whole Element becomes the target, and this causes the drop gesture to append.
54067  */
54068     getTargetFromEvent : function(e) {
54069                 var target = e.getTarget();
54070                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54071                 target = target.parentNode;
54072                 }
54073                 if (!target) {
54074                         target = this.el.dom.lastChild || this.el.dom;
54075                 }
54076                 return target;
54077     },
54078
54079 /**
54080  *      Create the drag data which consists of an object which has the property "ddel" as
54081  *      the drag proxy element. 
54082  */
54083     getDragData : function(e) {
54084         var target = this.findItemFromChild(e.getTarget());
54085                 if(target) {
54086                         this.handleSelection(e);
54087                         var selNodes = this.getSelectedNodes();
54088             var dragData = {
54089                 source: this,
54090                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54091                 nodes: selNodes,
54092                 records: []
54093                         };
54094                         var selectedIndices = this.getSelectedIndexes();
54095                         for (var i = 0; i < selectedIndices.length; i++) {
54096                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54097                         }
54098                         if (selNodes.length == 1) {
54099                                 dragData.ddel = target.cloneNode(true); // the div element
54100                         } else {
54101                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54102                                 div.className = 'multi-proxy';
54103                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54104                                         div.appendChild(selNodes[i].cloneNode(true));
54105                                 }
54106                                 dragData.ddel = div;
54107                         }
54108             //console.log(dragData)
54109             //console.log(dragData.ddel.innerHTML)
54110                         return dragData;
54111                 }
54112         //console.log('nodragData')
54113                 return false;
54114     },
54115     
54116 /**     Specify to which ddGroup items in this DDView may be dragged. */
54117     setDraggable: function(ddGroup) {
54118         if (ddGroup instanceof Array) {
54119                 Roo.each(ddGroup, this.setDraggable, this);
54120                 return;
54121         }
54122         if (this.dragZone) {
54123                 this.dragZone.addToGroup(ddGroup);
54124         } else {
54125                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54126                                 containerScroll: true,
54127                                 ddGroup: ddGroup 
54128
54129                         });
54130 //                      Draggability implies selection. DragZone's mousedown selects the element.
54131                         if (!this.multiSelect) { this.singleSelect = true; }
54132
54133 //                      Wire the DragZone's handlers up to methods in *this*
54134                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54135                 }
54136     },
54137
54138 /**     Specify from which ddGroup this DDView accepts drops. */
54139     setDroppable: function(ddGroup) {
54140         if (ddGroup instanceof Array) {
54141                 Roo.each(ddGroup, this.setDroppable, this);
54142                 return;
54143         }
54144         if (this.dropZone) {
54145                 this.dropZone.addToGroup(ddGroup);
54146         } else {
54147                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54148                                 containerScroll: true,
54149                                 ddGroup: ddGroup
54150                         });
54151
54152 //                      Wire the DropZone's handlers up to methods in *this*
54153                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54154                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54155                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54156                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54157                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54158                 }
54159     },
54160
54161 /**     Decide whether to drop above or below a View node. */
54162     getDropPoint : function(e, n, dd){
54163         if (n == this.el.dom) { return "above"; }
54164                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54165                 var c = t + (b - t) / 2;
54166                 var y = Roo.lib.Event.getPageY(e);
54167                 if(y <= c) {
54168                         return "above";
54169                 }else{
54170                         return "below";
54171                 }
54172     },
54173
54174     onNodeEnter : function(n, dd, e, data){
54175                 return false;
54176     },
54177     
54178     onNodeOver : function(n, dd, e, data){
54179                 var pt = this.getDropPoint(e, n, dd);
54180                 // set the insert point style on the target node
54181                 var dragElClass = this.dropNotAllowed;
54182                 if (pt) {
54183                         var targetElClass;
54184                         if (pt == "above"){
54185                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54186                                 targetElClass = "x-view-drag-insert-above";
54187                         } else {
54188                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54189                                 targetElClass = "x-view-drag-insert-below";
54190                         }
54191                         if (this.lastInsertClass != targetElClass){
54192                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54193                                 this.lastInsertClass = targetElClass;
54194                         }
54195                 }
54196                 return dragElClass;
54197         },
54198
54199     onNodeOut : function(n, dd, e, data){
54200                 this.removeDropIndicators(n);
54201     },
54202
54203     onNodeDrop : function(n, dd, e, data){
54204         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54205                 return false;
54206         }
54207         var pt = this.getDropPoint(e, n, dd);
54208                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54209                 if (pt == "below") { insertAt++; }
54210                 for (var i = 0; i < data.records.length; i++) {
54211                         var r = data.records[i];
54212                         var dup = this.store.getById(r.id);
54213                         if (dup && (dd != this.dragZone)) {
54214                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54215                         } else {
54216                                 if (data.copy) {
54217                                         this.store.insert(insertAt++, r.copy());
54218                                 } else {
54219                                         data.source.isDirtyFlag = true;
54220                                         r.store.remove(r);
54221                                         this.store.insert(insertAt++, r);
54222                                 }
54223                                 this.isDirtyFlag = true;
54224                         }
54225                 }
54226                 this.dragZone.cachedTarget = null;
54227                 return true;
54228     },
54229
54230     removeDropIndicators : function(n){
54231                 if(n){
54232                         Roo.fly(n).removeClass([
54233                                 "x-view-drag-insert-above",
54234                                 "x-view-drag-insert-below"]);
54235                         this.lastInsertClass = "_noclass";
54236                 }
54237     },
54238
54239 /**
54240  *      Utility method. Add a delete option to the DDView's context menu.
54241  *      @param {String} imageUrl The URL of the "delete" icon image.
54242  */
54243         setDeletable: function(imageUrl) {
54244                 if (!this.singleSelect && !this.multiSelect) {
54245                         this.singleSelect = true;
54246                 }
54247                 var c = this.getContextMenu();
54248                 this.contextMenu.on("itemclick", function(item) {
54249                         switch (item.id) {
54250                                 case "delete":
54251                                         this.remove(this.getSelectedIndexes());
54252                                         break;
54253                         }
54254                 }, this);
54255                 this.contextMenu.add({
54256                         icon: imageUrl,
54257                         id: "delete",
54258                         text: 'Delete'
54259                 });
54260         },
54261         
54262 /**     Return the context menu for this DDView. */
54263         getContextMenu: function() {
54264                 if (!this.contextMenu) {
54265 //                      Create the View's context menu
54266                         this.contextMenu = new Roo.menu.Menu({
54267                                 id: this.id + "-contextmenu"
54268                         });
54269                         this.el.on("contextmenu", this.showContextMenu, this);
54270                 }
54271                 return this.contextMenu;
54272         },
54273         
54274         disableContextMenu: function() {
54275                 if (this.contextMenu) {
54276                         this.el.un("contextmenu", this.showContextMenu, this);
54277                 }
54278         },
54279
54280         showContextMenu: function(e, item) {
54281         item = this.findItemFromChild(e.getTarget());
54282                 if (item) {
54283                         e.stopEvent();
54284                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54285                         this.contextMenu.showAt(e.getXY());
54286             }
54287     },
54288
54289 /**
54290  *      Remove {@link Roo.data.Record}s at the specified indices.
54291  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54292  */
54293     remove: function(selectedIndices) {
54294                 selectedIndices = [].concat(selectedIndices);
54295                 for (var i = 0; i < selectedIndices.length; i++) {
54296                         var rec = this.store.getAt(selectedIndices[i]);
54297                         this.store.remove(rec);
54298                 }
54299     },
54300
54301 /**
54302  *      Double click fires the event, but also, if this is draggable, and there is only one other
54303  *      related DropZone, it transfers the selected node.
54304  */
54305     onDblClick : function(e){
54306         var item = this.findItemFromChild(e.getTarget());
54307         if(item){
54308             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54309                 return false;
54310             }
54311             if (this.dragGroup) {
54312                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54313                     while (targets.indexOf(this.dropZone) > -1) {
54314                             targets.remove(this.dropZone);
54315                                 }
54316                     if (targets.length == 1) {
54317                                         this.dragZone.cachedTarget = null;
54318                         var el = Roo.get(targets[0].getEl());
54319                         var box = el.getBox(true);
54320                         targets[0].onNodeDrop(el.dom, {
54321                                 target: el.dom,
54322                                 xy: [box.x, box.y + box.height - 1]
54323                         }, null, this.getDragData(e));
54324                     }
54325                 }
54326         }
54327     },
54328     
54329     handleSelection: function(e) {
54330                 this.dragZone.cachedTarget = null;
54331         var item = this.findItemFromChild(e.getTarget());
54332         if (!item) {
54333                 this.clearSelections(true);
54334                 return;
54335         }
54336                 if (item && (this.multiSelect || this.singleSelect)){
54337                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54338                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54339                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54340                                 this.unselect(item);
54341                         } else {
54342                                 this.select(item, this.multiSelect && e.ctrlKey);
54343                                 this.lastSelection = item;
54344                         }
54345                 }
54346     },
54347
54348     onItemClick : function(item, index, e){
54349                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54350                         return false;
54351                 }
54352                 return true;
54353     },
54354
54355     unselect : function(nodeInfo, suppressEvent){
54356                 var node = this.getNode(nodeInfo);
54357                 if(node && this.isSelected(node)){
54358                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54359                                 Roo.fly(node).removeClass(this.selectedClass);
54360                                 this.selections.remove(node);
54361                                 if(!suppressEvent){
54362                                         this.fireEvent("selectionchange", this, this.selections);
54363                                 }
54364                         }
54365                 }
54366     }
54367 });
54368 /*
54369  * Based on:
54370  * Ext JS Library 1.1.1
54371  * Copyright(c) 2006-2007, Ext JS, LLC.
54372  *
54373  * Originally Released Under LGPL - original licence link has changed is not relivant.
54374  *
54375  * Fork - LGPL
54376  * <script type="text/javascript">
54377  */
54378  
54379 /**
54380  * @class Roo.LayoutManager
54381  * @extends Roo.util.Observable
54382  * Base class for layout managers.
54383  */
54384 Roo.LayoutManager = function(container, config){
54385     Roo.LayoutManager.superclass.constructor.call(this);
54386     this.el = Roo.get(container);
54387     // ie scrollbar fix
54388     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54389         document.body.scroll = "no";
54390     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54391         this.el.position('relative');
54392     }
54393     this.id = this.el.id;
54394     this.el.addClass("x-layout-container");
54395     /** false to disable window resize monitoring @type Boolean */
54396     this.monitorWindowResize = true;
54397     this.regions = {};
54398     this.addEvents({
54399         /**
54400          * @event layout
54401          * Fires when a layout is performed. 
54402          * @param {Roo.LayoutManager} this
54403          */
54404         "layout" : true,
54405         /**
54406          * @event regionresized
54407          * Fires when the user resizes a region. 
54408          * @param {Roo.LayoutRegion} region The resized region
54409          * @param {Number} newSize The new size (width for east/west, height for north/south)
54410          */
54411         "regionresized" : true,
54412         /**
54413          * @event regioncollapsed
54414          * Fires when a region is collapsed. 
54415          * @param {Roo.LayoutRegion} region The collapsed region
54416          */
54417         "regioncollapsed" : true,
54418         /**
54419          * @event regionexpanded
54420          * Fires when a region is expanded.  
54421          * @param {Roo.LayoutRegion} region The expanded region
54422          */
54423         "regionexpanded" : true
54424     });
54425     this.updating = false;
54426     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54427 };
54428
54429 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54430     /**
54431      * Returns true if this layout is currently being updated
54432      * @return {Boolean}
54433      */
54434     isUpdating : function(){
54435         return this.updating; 
54436     },
54437     
54438     /**
54439      * Suspend the LayoutManager from doing auto-layouts while
54440      * making multiple add or remove calls
54441      */
54442     beginUpdate : function(){
54443         this.updating = true;    
54444     },
54445     
54446     /**
54447      * Restore auto-layouts and optionally disable the manager from performing a layout
54448      * @param {Boolean} noLayout true to disable a layout update 
54449      */
54450     endUpdate : function(noLayout){
54451         this.updating = false;
54452         if(!noLayout){
54453             this.layout();
54454         }    
54455     },
54456     
54457     layout: function(){
54458         
54459     },
54460     
54461     onRegionResized : function(region, newSize){
54462         this.fireEvent("regionresized", region, newSize);
54463         this.layout();
54464     },
54465     
54466     onRegionCollapsed : function(region){
54467         this.fireEvent("regioncollapsed", region);
54468     },
54469     
54470     onRegionExpanded : function(region){
54471         this.fireEvent("regionexpanded", region);
54472     },
54473         
54474     /**
54475      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54476      * performs box-model adjustments.
54477      * @return {Object} The size as an object {width: (the width), height: (the height)}
54478      */
54479     getViewSize : function(){
54480         var size;
54481         if(this.el.dom != document.body){
54482             size = this.el.getSize();
54483         }else{
54484             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54485         }
54486         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54487         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54488         return size;
54489     },
54490     
54491     /**
54492      * Returns the Element this layout is bound to.
54493      * @return {Roo.Element}
54494      */
54495     getEl : function(){
54496         return this.el;
54497     },
54498     
54499     /**
54500      * Returns the specified region.
54501      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54502      * @return {Roo.LayoutRegion}
54503      */
54504     getRegion : function(target){
54505         return this.regions[target.toLowerCase()];
54506     },
54507     
54508     onWindowResize : function(){
54509         if(this.monitorWindowResize){
54510             this.layout();
54511         }
54512     }
54513 });/*
54514  * Based on:
54515  * Ext JS Library 1.1.1
54516  * Copyright(c) 2006-2007, Ext JS, LLC.
54517  *
54518  * Originally Released Under LGPL - original licence link has changed is not relivant.
54519  *
54520  * Fork - LGPL
54521  * <script type="text/javascript">
54522  */
54523 /**
54524  * @class Roo.BorderLayout
54525  * @extends Roo.LayoutManager
54526  * @children Roo.ContentPanel
54527  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54528  * please see: <br><br>
54529  * <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>
54530  * <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>
54531  * Example:
54532  <pre><code>
54533  var layout = new Roo.BorderLayout(document.body, {
54534     north: {
54535         initialSize: 25,
54536         titlebar: false
54537     },
54538     west: {
54539         split:true,
54540         initialSize: 200,
54541         minSize: 175,
54542         maxSize: 400,
54543         titlebar: true,
54544         collapsible: true
54545     },
54546     east: {
54547         split:true,
54548         initialSize: 202,
54549         minSize: 175,
54550         maxSize: 400,
54551         titlebar: true,
54552         collapsible: true
54553     },
54554     south: {
54555         split:true,
54556         initialSize: 100,
54557         minSize: 100,
54558         maxSize: 200,
54559         titlebar: true,
54560         collapsible: true
54561     },
54562     center: {
54563         titlebar: true,
54564         autoScroll:true,
54565         resizeTabs: true,
54566         minTabWidth: 50,
54567         preferredTabWidth: 150
54568     }
54569 });
54570
54571 // shorthand
54572 var CP = Roo.ContentPanel;
54573
54574 layout.beginUpdate();
54575 layout.add("north", new CP("north", "North"));
54576 layout.add("south", new CP("south", {title: "South", closable: true}));
54577 layout.add("west", new CP("west", {title: "West"}));
54578 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54579 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54580 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54581 layout.getRegion("center").showPanel("center1");
54582 layout.endUpdate();
54583 </code></pre>
54584
54585 <b>The container the layout is rendered into can be either the body element or any other element.
54586 If it is not the body element, the container needs to either be an absolute positioned element,
54587 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54588 the container size if it is not the body element.</b>
54589
54590 * @constructor
54591 * Create a new BorderLayout
54592 * @param {String/HTMLElement/Element} container The container this layout is bound to
54593 * @param {Object} config Configuration options
54594  */
54595 Roo.BorderLayout = function(container, config){
54596     config = config || {};
54597     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54598     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54599     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54600         var target = this.factory.validRegions[i];
54601         if(config[target]){
54602             this.addRegion(target, config[target]);
54603         }
54604     }
54605 };
54606
54607 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54608         
54609         /**
54610          * @cfg {Roo.LayoutRegion} east
54611          */
54612         /**
54613          * @cfg {Roo.LayoutRegion} west
54614          */
54615         /**
54616          * @cfg {Roo.LayoutRegion} north
54617          */
54618         /**
54619          * @cfg {Roo.LayoutRegion} south
54620          */
54621         /**
54622          * @cfg {Roo.LayoutRegion} center
54623          */
54624     /**
54625      * Creates and adds a new region if it doesn't already exist.
54626      * @param {String} target The target region key (north, south, east, west or center).
54627      * @param {Object} config The regions config object
54628      * @return {BorderLayoutRegion} The new region
54629      */
54630     addRegion : function(target, config){
54631         if(!this.regions[target]){
54632             var r = this.factory.create(target, this, config);
54633             this.bindRegion(target, r);
54634         }
54635         return this.regions[target];
54636     },
54637
54638     // private (kinda)
54639     bindRegion : function(name, r){
54640         this.regions[name] = r;
54641         r.on("visibilitychange", this.layout, this);
54642         r.on("paneladded", this.layout, this);
54643         r.on("panelremoved", this.layout, this);
54644         r.on("invalidated", this.layout, this);
54645         r.on("resized", this.onRegionResized, this);
54646         r.on("collapsed", this.onRegionCollapsed, this);
54647         r.on("expanded", this.onRegionExpanded, this);
54648     },
54649
54650     /**
54651      * Performs a layout update.
54652      */
54653     layout : function(){
54654         if(this.updating) {
54655             return;
54656         }
54657         var size = this.getViewSize();
54658         var w = size.width;
54659         var h = size.height;
54660         var centerW = w;
54661         var centerH = h;
54662         var centerY = 0;
54663         var centerX = 0;
54664         //var x = 0, y = 0;
54665
54666         var rs = this.regions;
54667         var north = rs["north"];
54668         var south = rs["south"]; 
54669         var west = rs["west"];
54670         var east = rs["east"];
54671         var center = rs["center"];
54672         //if(this.hideOnLayout){ // not supported anymore
54673             //c.el.setStyle("display", "none");
54674         //}
54675         if(north && north.isVisible()){
54676             var b = north.getBox();
54677             var m = north.getMargins();
54678             b.width = w - (m.left+m.right);
54679             b.x = m.left;
54680             b.y = m.top;
54681             centerY = b.height + b.y + m.bottom;
54682             centerH -= centerY;
54683             north.updateBox(this.safeBox(b));
54684         }
54685         if(south && south.isVisible()){
54686             var b = south.getBox();
54687             var m = south.getMargins();
54688             b.width = w - (m.left+m.right);
54689             b.x = m.left;
54690             var totalHeight = (b.height + m.top + m.bottom);
54691             b.y = h - totalHeight + m.top;
54692             centerH -= totalHeight;
54693             south.updateBox(this.safeBox(b));
54694         }
54695         if(west && west.isVisible()){
54696             var b = west.getBox();
54697             var m = west.getMargins();
54698             b.height = centerH - (m.top+m.bottom);
54699             b.x = m.left;
54700             b.y = centerY + m.top;
54701             var totalWidth = (b.width + m.left + m.right);
54702             centerX += totalWidth;
54703             centerW -= totalWidth;
54704             west.updateBox(this.safeBox(b));
54705         }
54706         if(east && east.isVisible()){
54707             var b = east.getBox();
54708             var m = east.getMargins();
54709             b.height = centerH - (m.top+m.bottom);
54710             var totalWidth = (b.width + m.left + m.right);
54711             b.x = w - totalWidth + m.left;
54712             b.y = centerY + m.top;
54713             centerW -= totalWidth;
54714             east.updateBox(this.safeBox(b));
54715         }
54716         if(center){
54717             var m = center.getMargins();
54718             var centerBox = {
54719                 x: centerX + m.left,
54720                 y: centerY + m.top,
54721                 width: centerW - (m.left+m.right),
54722                 height: centerH - (m.top+m.bottom)
54723             };
54724             //if(this.hideOnLayout){
54725                 //center.el.setStyle("display", "block");
54726             //}
54727             center.updateBox(this.safeBox(centerBox));
54728         }
54729         this.el.repaint();
54730         this.fireEvent("layout", this);
54731     },
54732
54733     // private
54734     safeBox : function(box){
54735         box.width = Math.max(0, box.width);
54736         box.height = Math.max(0, box.height);
54737         return box;
54738     },
54739
54740     /**
54741      * Adds a ContentPanel (or subclass) to this layout.
54742      * @param {String} target The target region key (north, south, east, west or center).
54743      * @param {Roo.ContentPanel} panel The panel to add
54744      * @return {Roo.ContentPanel} The added panel
54745      */
54746     add : function(target, panel){
54747          
54748         target = target.toLowerCase();
54749         return this.regions[target].add(panel);
54750     },
54751
54752     /**
54753      * Remove a ContentPanel (or subclass) to this layout.
54754      * @param {String} target The target region key (north, south, east, west or center).
54755      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54756      * @return {Roo.ContentPanel} The removed panel
54757      */
54758     remove : function(target, panel){
54759         target = target.toLowerCase();
54760         return this.regions[target].remove(panel);
54761     },
54762
54763     /**
54764      * Searches all regions for a panel with the specified id
54765      * @param {String} panelId
54766      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54767      */
54768     findPanel : function(panelId){
54769         var rs = this.regions;
54770         for(var target in rs){
54771             if(typeof rs[target] != "function"){
54772                 var p = rs[target].getPanel(panelId);
54773                 if(p){
54774                     return p;
54775                 }
54776             }
54777         }
54778         return null;
54779     },
54780
54781     /**
54782      * Searches all regions for a panel with the specified id and activates (shows) it.
54783      * @param {String/ContentPanel} panelId The panels id or the panel itself
54784      * @return {Roo.ContentPanel} The shown panel or null
54785      */
54786     showPanel : function(panelId) {
54787       var rs = this.regions;
54788       for(var target in rs){
54789          var r = rs[target];
54790          if(typeof r != "function"){
54791             if(r.hasPanel(panelId)){
54792                return r.showPanel(panelId);
54793             }
54794          }
54795       }
54796       return null;
54797    },
54798
54799    /**
54800      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54801      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54802      */
54803     restoreState : function(provider){
54804         if(!provider){
54805             provider = Roo.state.Manager;
54806         }
54807         var sm = new Roo.LayoutStateManager();
54808         sm.init(this, provider);
54809     },
54810
54811     /**
54812      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54813      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54814      * a valid ContentPanel config object.  Example:
54815      * <pre><code>
54816 // Create the main layout
54817 var layout = new Roo.BorderLayout('main-ct', {
54818     west: {
54819         split:true,
54820         minSize: 175,
54821         titlebar: true
54822     },
54823     center: {
54824         title:'Components'
54825     }
54826 }, 'main-ct');
54827
54828 // Create and add multiple ContentPanels at once via configs
54829 layout.batchAdd({
54830    west: {
54831        id: 'source-files',
54832        autoCreate:true,
54833        title:'Ext Source Files',
54834        autoScroll:true,
54835        fitToFrame:true
54836    },
54837    center : {
54838        el: cview,
54839        autoScroll:true,
54840        fitToFrame:true,
54841        toolbar: tb,
54842        resizeEl:'cbody'
54843    }
54844 });
54845 </code></pre>
54846      * @param {Object} regions An object containing ContentPanel configs by region name
54847      */
54848     batchAdd : function(regions){
54849         this.beginUpdate();
54850         for(var rname in regions){
54851             var lr = this.regions[rname];
54852             if(lr){
54853                 this.addTypedPanels(lr, regions[rname]);
54854             }
54855         }
54856         this.endUpdate();
54857     },
54858
54859     // private
54860     addTypedPanels : function(lr, ps){
54861         if(typeof ps == 'string'){
54862             lr.add(new Roo.ContentPanel(ps));
54863         }
54864         else if(ps instanceof Array){
54865             for(var i =0, len = ps.length; i < len; i++){
54866                 this.addTypedPanels(lr, ps[i]);
54867             }
54868         }
54869         else if(!ps.events){ // raw config?
54870             var el = ps.el;
54871             delete ps.el; // prevent conflict
54872             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54873         }
54874         else {  // panel object assumed!
54875             lr.add(ps);
54876         }
54877     },
54878     /**
54879      * Adds a xtype elements to the layout.
54880      * <pre><code>
54881
54882 layout.addxtype({
54883        xtype : 'ContentPanel',
54884        region: 'west',
54885        items: [ .... ]
54886    }
54887 );
54888
54889 layout.addxtype({
54890         xtype : 'NestedLayoutPanel',
54891         region: 'west',
54892         layout: {
54893            center: { },
54894            west: { }   
54895         },
54896         items : [ ... list of content panels or nested layout panels.. ]
54897    }
54898 );
54899 </code></pre>
54900      * @param {Object} cfg Xtype definition of item to add.
54901      */
54902     addxtype : function(cfg)
54903     {
54904         // basically accepts a pannel...
54905         // can accept a layout region..!?!?
54906         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54907         
54908         if (!cfg.xtype.match(/Panel$/)) {
54909             return false;
54910         }
54911         var ret = false;
54912         
54913         if (typeof(cfg.region) == 'undefined') {
54914             Roo.log("Failed to add Panel, region was not set");
54915             Roo.log(cfg);
54916             return false;
54917         }
54918         var region = cfg.region;
54919         delete cfg.region;
54920         
54921           
54922         var xitems = [];
54923         if (cfg.items) {
54924             xitems = cfg.items;
54925             delete cfg.items;
54926         }
54927         var nb = false;
54928         
54929         switch(cfg.xtype) 
54930         {
54931             case 'ContentPanel':  // ContentPanel (el, cfg)
54932             case 'ScrollPanel':  // ContentPanel (el, cfg)
54933             case 'ViewPanel': 
54934                 if(cfg.autoCreate) {
54935                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54936                 } else {
54937                     var el = this.el.createChild();
54938                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54939                 }
54940                 
54941                 this.add(region, ret);
54942                 break;
54943             
54944             
54945             case 'TreePanel': // our new panel!
54946                 cfg.el = this.el.createChild();
54947                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54948                 this.add(region, ret);
54949                 break;
54950             
54951             case 'NestedLayoutPanel': 
54952                 // create a new Layout (which is  a Border Layout...
54953                 var el = this.el.createChild();
54954                 var clayout = cfg.layout;
54955                 delete cfg.layout;
54956                 clayout.items   = clayout.items  || [];
54957                 // replace this exitems with the clayout ones..
54958                 xitems = clayout.items;
54959                  
54960                 
54961                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54962                     cfg.background = false;
54963                 }
54964                 var layout = new Roo.BorderLayout(el, clayout);
54965                 
54966                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54967                 //console.log('adding nested layout panel '  + cfg.toSource());
54968                 this.add(region, ret);
54969                 nb = {}; /// find first...
54970                 break;
54971                 
54972             case 'GridPanel': 
54973             
54974                 // needs grid and region
54975                 
54976                 //var el = this.getRegion(region).el.createChild();
54977                 var el = this.el.createChild();
54978                 // create the grid first...
54979                 
54980                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
54981                 delete cfg.grid;
54982                 if (region == 'center' && this.active ) {
54983                     cfg.background = false;
54984                 }
54985                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
54986                 
54987                 this.add(region, ret);
54988                 if (cfg.background) {
54989                     ret.on('activate', function(gp) {
54990                         if (!gp.grid.rendered) {
54991                             gp.grid.render();
54992                         }
54993                     });
54994                 } else {
54995                     grid.render();
54996                 }
54997                 break;
54998            
54999            
55000            
55001                 
55002                 
55003                 
55004             default:
55005                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55006                     
55007                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55008                     this.add(region, ret);
55009                 } else {
55010                 
55011                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55012                     return null;
55013                 }
55014                 
55015              // GridPanel (grid, cfg)
55016             
55017         }
55018         this.beginUpdate();
55019         // add children..
55020         var region = '';
55021         var abn = {};
55022         Roo.each(xitems, function(i)  {
55023             region = nb && i.region ? i.region : false;
55024             
55025             var add = ret.addxtype(i);
55026            
55027             if (region) {
55028                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55029                 if (!i.background) {
55030                     abn[region] = nb[region] ;
55031                 }
55032             }
55033             
55034         });
55035         this.endUpdate();
55036
55037         // make the last non-background panel active..
55038         //if (nb) { Roo.log(abn); }
55039         if (nb) {
55040             
55041             for(var r in abn) {
55042                 region = this.getRegion(r);
55043                 if (region) {
55044                     // tried using nb[r], but it does not work..
55045                      
55046                     region.showPanel(abn[r]);
55047                    
55048                 }
55049             }
55050         }
55051         return ret;
55052         
55053     }
55054 });
55055
55056 /**
55057  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55058  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55059  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55060  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55061  * <pre><code>
55062 // shorthand
55063 var CP = Roo.ContentPanel;
55064
55065 var layout = Roo.BorderLayout.create({
55066     north: {
55067         initialSize: 25,
55068         titlebar: false,
55069         panels: [new CP("north", "North")]
55070     },
55071     west: {
55072         split:true,
55073         initialSize: 200,
55074         minSize: 175,
55075         maxSize: 400,
55076         titlebar: true,
55077         collapsible: true,
55078         panels: [new CP("west", {title: "West"})]
55079     },
55080     east: {
55081         split:true,
55082         initialSize: 202,
55083         minSize: 175,
55084         maxSize: 400,
55085         titlebar: true,
55086         collapsible: true,
55087         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55088     },
55089     south: {
55090         split:true,
55091         initialSize: 100,
55092         minSize: 100,
55093         maxSize: 200,
55094         titlebar: true,
55095         collapsible: true,
55096         panels: [new CP("south", {title: "South", closable: true})]
55097     },
55098     center: {
55099         titlebar: true,
55100         autoScroll:true,
55101         resizeTabs: true,
55102         minTabWidth: 50,
55103         preferredTabWidth: 150,
55104         panels: [
55105             new CP("center1", {title: "Close Me", closable: true}),
55106             new CP("center2", {title: "Center Panel", closable: false})
55107         ]
55108     }
55109 }, document.body);
55110
55111 layout.getRegion("center").showPanel("center1");
55112 </code></pre>
55113  * @param config
55114  * @param targetEl
55115  */
55116 Roo.BorderLayout.create = function(config, targetEl){
55117     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55118     layout.beginUpdate();
55119     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55120     for(var j = 0, jlen = regions.length; j < jlen; j++){
55121         var lr = regions[j];
55122         if(layout.regions[lr] && config[lr].panels){
55123             var r = layout.regions[lr];
55124             var ps = config[lr].panels;
55125             layout.addTypedPanels(r, ps);
55126         }
55127     }
55128     layout.endUpdate();
55129     return layout;
55130 };
55131
55132 // private
55133 Roo.BorderLayout.RegionFactory = {
55134     // private
55135     validRegions : ["north","south","east","west","center"],
55136
55137     // private
55138     create : function(target, mgr, config){
55139         target = target.toLowerCase();
55140         if(config.lightweight || config.basic){
55141             return new Roo.BasicLayoutRegion(mgr, config, target);
55142         }
55143         switch(target){
55144             case "north":
55145                 return new Roo.NorthLayoutRegion(mgr, config);
55146             case "south":
55147                 return new Roo.SouthLayoutRegion(mgr, config);
55148             case "east":
55149                 return new Roo.EastLayoutRegion(mgr, config);
55150             case "west":
55151                 return new Roo.WestLayoutRegion(mgr, config);
55152             case "center":
55153                 return new Roo.CenterLayoutRegion(mgr, config);
55154         }
55155         throw 'Layout region "'+target+'" not supported.';
55156     }
55157 };/*
55158  * Based on:
55159  * Ext JS Library 1.1.1
55160  * Copyright(c) 2006-2007, Ext JS, LLC.
55161  *
55162  * Originally Released Under LGPL - original licence link has changed is not relivant.
55163  *
55164  * Fork - LGPL
55165  * <script type="text/javascript">
55166  */
55167  
55168 /**
55169  * @class Roo.BasicLayoutRegion
55170  * @extends Roo.util.Observable
55171  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55172  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55173  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55174  */
55175 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55176     this.mgr = mgr;
55177     this.position  = pos;
55178     this.events = {
55179         /**
55180          * @scope Roo.BasicLayoutRegion
55181          */
55182         
55183         /**
55184          * @event beforeremove
55185          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55186          * @param {Roo.LayoutRegion} this
55187          * @param {Roo.ContentPanel} panel The panel
55188          * @param {Object} e The cancel event object
55189          */
55190         "beforeremove" : true,
55191         /**
55192          * @event invalidated
55193          * Fires when the layout for this region is changed.
55194          * @param {Roo.LayoutRegion} this
55195          */
55196         "invalidated" : true,
55197         /**
55198          * @event visibilitychange
55199          * Fires when this region is shown or hidden 
55200          * @param {Roo.LayoutRegion} this
55201          * @param {Boolean} visibility true or false
55202          */
55203         "visibilitychange" : true,
55204         /**
55205          * @event paneladded
55206          * Fires when a panel is added. 
55207          * @param {Roo.LayoutRegion} this
55208          * @param {Roo.ContentPanel} panel The panel
55209          */
55210         "paneladded" : true,
55211         /**
55212          * @event panelremoved
55213          * Fires when a panel is removed. 
55214          * @param {Roo.LayoutRegion} this
55215          * @param {Roo.ContentPanel} panel The panel
55216          */
55217         "panelremoved" : true,
55218         /**
55219          * @event beforecollapse
55220          * Fires when this region before collapse.
55221          * @param {Roo.LayoutRegion} this
55222          */
55223         "beforecollapse" : true,
55224         /**
55225          * @event collapsed
55226          * Fires when this region is collapsed.
55227          * @param {Roo.LayoutRegion} this
55228          */
55229         "collapsed" : true,
55230         /**
55231          * @event expanded
55232          * Fires when this region is expanded.
55233          * @param {Roo.LayoutRegion} this
55234          */
55235         "expanded" : true,
55236         /**
55237          * @event slideshow
55238          * Fires when this region is slid into view.
55239          * @param {Roo.LayoutRegion} this
55240          */
55241         "slideshow" : true,
55242         /**
55243          * @event slidehide
55244          * Fires when this region slides out of view. 
55245          * @param {Roo.LayoutRegion} this
55246          */
55247         "slidehide" : true,
55248         /**
55249          * @event panelactivated
55250          * Fires when a panel is activated. 
55251          * @param {Roo.LayoutRegion} this
55252          * @param {Roo.ContentPanel} panel The activated panel
55253          */
55254         "panelactivated" : true,
55255         /**
55256          * @event resized
55257          * Fires when the user resizes this region. 
55258          * @param {Roo.LayoutRegion} this
55259          * @param {Number} newSize The new size (width for east/west, height for north/south)
55260          */
55261         "resized" : true
55262     };
55263     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55264     this.panels = new Roo.util.MixedCollection();
55265     this.panels.getKey = this.getPanelId.createDelegate(this);
55266     this.box = null;
55267     this.activePanel = null;
55268     // ensure listeners are added...
55269     
55270     if (config.listeners || config.events) {
55271         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55272             listeners : config.listeners || {},
55273             events : config.events || {}
55274         });
55275     }
55276     
55277     if(skipConfig !== true){
55278         this.applyConfig(config);
55279     }
55280 };
55281
55282 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55283     getPanelId : function(p){
55284         return p.getId();
55285     },
55286     
55287     applyConfig : function(config){
55288         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55289         this.config = config;
55290         
55291     },
55292     
55293     /**
55294      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55295      * the width, for horizontal (north, south) the height.
55296      * @param {Number} newSize The new width or height
55297      */
55298     resizeTo : function(newSize){
55299         var el = this.el ? this.el :
55300                  (this.activePanel ? this.activePanel.getEl() : null);
55301         if(el){
55302             switch(this.position){
55303                 case "east":
55304                 case "west":
55305                     el.setWidth(newSize);
55306                     this.fireEvent("resized", this, newSize);
55307                 break;
55308                 case "north":
55309                 case "south":
55310                     el.setHeight(newSize);
55311                     this.fireEvent("resized", this, newSize);
55312                 break;                
55313             }
55314         }
55315     },
55316     
55317     getBox : function(){
55318         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55319     },
55320     
55321     getMargins : function(){
55322         return this.margins;
55323     },
55324     
55325     updateBox : function(box){
55326         this.box = box;
55327         var el = this.activePanel.getEl();
55328         el.dom.style.left = box.x + "px";
55329         el.dom.style.top = box.y + "px";
55330         this.activePanel.setSize(box.width, box.height);
55331     },
55332     
55333     /**
55334      * Returns the container element for this region.
55335      * @return {Roo.Element}
55336      */
55337     getEl : function(){
55338         return this.activePanel;
55339     },
55340     
55341     /**
55342      * Returns true if this region is currently visible.
55343      * @return {Boolean}
55344      */
55345     isVisible : function(){
55346         return this.activePanel ? true : false;
55347     },
55348     
55349     setActivePanel : function(panel){
55350         panel = this.getPanel(panel);
55351         if(this.activePanel && this.activePanel != panel){
55352             this.activePanel.setActiveState(false);
55353             this.activePanel.getEl().setLeftTop(-10000,-10000);
55354         }
55355         this.activePanel = panel;
55356         panel.setActiveState(true);
55357         if(this.box){
55358             panel.setSize(this.box.width, this.box.height);
55359         }
55360         this.fireEvent("panelactivated", this, panel);
55361         this.fireEvent("invalidated");
55362     },
55363     
55364     /**
55365      * Show the specified panel.
55366      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55367      * @return {Roo.ContentPanel} The shown panel or null
55368      */
55369     showPanel : function(panel){
55370         if(panel = this.getPanel(panel)){
55371             this.setActivePanel(panel);
55372         }
55373         return panel;
55374     },
55375     
55376     /**
55377      * Get the active panel for this region.
55378      * @return {Roo.ContentPanel} The active panel or null
55379      */
55380     getActivePanel : function(){
55381         return this.activePanel;
55382     },
55383     
55384     /**
55385      * Add the passed ContentPanel(s)
55386      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55387      * @return {Roo.ContentPanel} The panel added (if only one was added)
55388      */
55389     add : function(panel){
55390         if(arguments.length > 1){
55391             for(var i = 0, len = arguments.length; i < len; i++) {
55392                 this.add(arguments[i]);
55393             }
55394             return null;
55395         }
55396         if(this.hasPanel(panel)){
55397             this.showPanel(panel);
55398             return panel;
55399         }
55400         var el = panel.getEl();
55401         if(el.dom.parentNode != this.mgr.el.dom){
55402             this.mgr.el.dom.appendChild(el.dom);
55403         }
55404         if(panel.setRegion){
55405             panel.setRegion(this);
55406         }
55407         this.panels.add(panel);
55408         el.setStyle("position", "absolute");
55409         if(!panel.background){
55410             this.setActivePanel(panel);
55411             if(this.config.initialSize && this.panels.getCount()==1){
55412                 this.resizeTo(this.config.initialSize);
55413             }
55414         }
55415         this.fireEvent("paneladded", this, panel);
55416         return panel;
55417     },
55418     
55419     /**
55420      * Returns true if the panel is in this region.
55421      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55422      * @return {Boolean}
55423      */
55424     hasPanel : function(panel){
55425         if(typeof panel == "object"){ // must be panel obj
55426             panel = panel.getId();
55427         }
55428         return this.getPanel(panel) ? true : false;
55429     },
55430     
55431     /**
55432      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55433      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55434      * @param {Boolean} preservePanel Overrides the config preservePanel option
55435      * @return {Roo.ContentPanel} The panel that was removed
55436      */
55437     remove : function(panel, preservePanel){
55438         panel = this.getPanel(panel);
55439         if(!panel){
55440             return null;
55441         }
55442         var e = {};
55443         this.fireEvent("beforeremove", this, panel, e);
55444         if(e.cancel === true){
55445             return null;
55446         }
55447         var panelId = panel.getId();
55448         this.panels.removeKey(panelId);
55449         return panel;
55450     },
55451     
55452     /**
55453      * Returns the panel specified or null if it's not in this region.
55454      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55455      * @return {Roo.ContentPanel}
55456      */
55457     getPanel : function(id){
55458         if(typeof id == "object"){ // must be panel obj
55459             return id;
55460         }
55461         return this.panels.get(id);
55462     },
55463     
55464     /**
55465      * Returns this regions position (north/south/east/west/center).
55466      * @return {String} 
55467      */
55468     getPosition: function(){
55469         return this.position;    
55470     }
55471 });/*
55472  * Based on:
55473  * Ext JS Library 1.1.1
55474  * Copyright(c) 2006-2007, Ext JS, LLC.
55475  *
55476  * Originally Released Under LGPL - original licence link has changed is not relivant.
55477  *
55478  * Fork - LGPL
55479  * <script type="text/javascript">
55480  */
55481  
55482 /**
55483  * @class Roo.LayoutRegion
55484  * @extends Roo.BasicLayoutRegion
55485  * This class represents a region in a layout manager.
55486  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55487  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55488  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55489  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55490  * @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})
55491  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55492  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55493  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55494  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55495  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55496  * @cfg {String}    title           The title for the region (overrides panel titles)
55497  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55498  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55499  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55500  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55501  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55502  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55503  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55504  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55505  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55506  * @cfg {Boolean}   showPin         True to show a pin button
55507  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55508  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55509  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55510  * @cfg {Number}    width           For East/West panels
55511  * @cfg {Number}    height          For North/South panels
55512  * @cfg {Boolean}   split           To show the splitter
55513  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55514  */
55515 Roo.LayoutRegion = function(mgr, config, pos){
55516     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55517     var dh = Roo.DomHelper;
55518     /** This region's container element 
55519     * @type Roo.Element */
55520     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55521     /** This region's title element 
55522     * @type Roo.Element */
55523
55524     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55525         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55526         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55527     ]}, true);
55528     this.titleEl.enableDisplayMode();
55529     /** This region's title text element 
55530     * @type HTMLElement */
55531     this.titleTextEl = this.titleEl.dom.firstChild;
55532     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55533     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55534     this.closeBtn.enableDisplayMode();
55535     this.closeBtn.on("click", this.closeClicked, this);
55536     this.closeBtn.hide();
55537
55538     this.createBody(config);
55539     this.visible = true;
55540     this.collapsed = false;
55541
55542     if(config.hideWhenEmpty){
55543         this.hide();
55544         this.on("paneladded", this.validateVisibility, this);
55545         this.on("panelremoved", this.validateVisibility, this);
55546     }
55547     this.applyConfig(config);
55548 };
55549
55550 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55551
55552     createBody : function(){
55553         /** This region's body element 
55554         * @type Roo.Element */
55555         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55556     },
55557
55558     applyConfig : function(c){
55559         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55560             var dh = Roo.DomHelper;
55561             if(c.titlebar !== false){
55562                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55563                 this.collapseBtn.on("click", this.collapse, this);
55564                 this.collapseBtn.enableDisplayMode();
55565
55566                 if(c.showPin === true || this.showPin){
55567                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55568                     this.stickBtn.enableDisplayMode();
55569                     this.stickBtn.on("click", this.expand, this);
55570                     this.stickBtn.hide();
55571                 }
55572             }
55573             /** This region's collapsed element
55574             * @type Roo.Element */
55575             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55576                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55577             ]}, true);
55578             if(c.floatable !== false){
55579                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55580                this.collapsedEl.on("click", this.collapseClick, this);
55581             }
55582
55583             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55584                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55585                    id: "message", unselectable: "on", style:{"float":"left"}});
55586                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55587              }
55588             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55589             this.expandBtn.on("click", this.expand, this);
55590         }
55591         if(this.collapseBtn){
55592             this.collapseBtn.setVisible(c.collapsible == true);
55593         }
55594         this.cmargins = c.cmargins || this.cmargins ||
55595                          (this.position == "west" || this.position == "east" ?
55596                              {top: 0, left: 2, right:2, bottom: 0} :
55597                              {top: 2, left: 0, right:0, bottom: 2});
55598         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55599         this.bottomTabs = c.tabPosition != "top";
55600         this.autoScroll = c.autoScroll || false;
55601         if(this.autoScroll){
55602             this.bodyEl.setStyle("overflow", "auto");
55603         }else{
55604             this.bodyEl.setStyle("overflow", "hidden");
55605         }
55606         //if(c.titlebar !== false){
55607             if((!c.titlebar && !c.title) || c.titlebar === false){
55608                 this.titleEl.hide();
55609             }else{
55610                 this.titleEl.show();
55611                 if(c.title){
55612                     this.titleTextEl.innerHTML = c.title;
55613                 }
55614             }
55615         //}
55616         this.duration = c.duration || .30;
55617         this.slideDuration = c.slideDuration || .45;
55618         this.config = c;
55619         if(c.collapsed){
55620             this.collapse(true);
55621         }
55622         if(c.hidden){
55623             this.hide();
55624         }
55625     },
55626     /**
55627      * Returns true if this region is currently visible.
55628      * @return {Boolean}
55629      */
55630     isVisible : function(){
55631         return this.visible;
55632     },
55633
55634     /**
55635      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55636      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55637      */
55638     setCollapsedTitle : function(title){
55639         title = title || "&#160;";
55640         if(this.collapsedTitleTextEl){
55641             this.collapsedTitleTextEl.innerHTML = title;
55642         }
55643     },
55644
55645     getBox : function(){
55646         var b;
55647         if(!this.collapsed){
55648             b = this.el.getBox(false, true);
55649         }else{
55650             b = this.collapsedEl.getBox(false, true);
55651         }
55652         return b;
55653     },
55654
55655     getMargins : function(){
55656         return this.collapsed ? this.cmargins : this.margins;
55657     },
55658
55659     highlight : function(){
55660         this.el.addClass("x-layout-panel-dragover");
55661     },
55662
55663     unhighlight : function(){
55664         this.el.removeClass("x-layout-panel-dragover");
55665     },
55666
55667     updateBox : function(box){
55668         this.box = box;
55669         if(!this.collapsed){
55670             this.el.dom.style.left = box.x + "px";
55671             this.el.dom.style.top = box.y + "px";
55672             this.updateBody(box.width, box.height);
55673         }else{
55674             this.collapsedEl.dom.style.left = box.x + "px";
55675             this.collapsedEl.dom.style.top = box.y + "px";
55676             this.collapsedEl.setSize(box.width, box.height);
55677         }
55678         if(this.tabs){
55679             this.tabs.autoSizeTabs();
55680         }
55681     },
55682
55683     updateBody : function(w, h){
55684         if(w !== null){
55685             this.el.setWidth(w);
55686             w -= this.el.getBorderWidth("rl");
55687             if(this.config.adjustments){
55688                 w += this.config.adjustments[0];
55689             }
55690         }
55691         if(h !== null){
55692             this.el.setHeight(h);
55693             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55694             h -= this.el.getBorderWidth("tb");
55695             if(this.config.adjustments){
55696                 h += this.config.adjustments[1];
55697             }
55698             this.bodyEl.setHeight(h);
55699             if(this.tabs){
55700                 h = this.tabs.syncHeight(h);
55701             }
55702         }
55703         if(this.panelSize){
55704             w = w !== null ? w : this.panelSize.width;
55705             h = h !== null ? h : this.panelSize.height;
55706         }
55707         if(this.activePanel){
55708             var el = this.activePanel.getEl();
55709             w = w !== null ? w : el.getWidth();
55710             h = h !== null ? h : el.getHeight();
55711             this.panelSize = {width: w, height: h};
55712             this.activePanel.setSize(w, h);
55713         }
55714         if(Roo.isIE && this.tabs){
55715             this.tabs.el.repaint();
55716         }
55717     },
55718
55719     /**
55720      * Returns the container element for this region.
55721      * @return {Roo.Element}
55722      */
55723     getEl : function(){
55724         return this.el;
55725     },
55726
55727     /**
55728      * Hides this region.
55729      */
55730     hide : function(){
55731         if(!this.collapsed){
55732             this.el.dom.style.left = "-2000px";
55733             this.el.hide();
55734         }else{
55735             this.collapsedEl.dom.style.left = "-2000px";
55736             this.collapsedEl.hide();
55737         }
55738         this.visible = false;
55739         this.fireEvent("visibilitychange", this, false);
55740     },
55741
55742     /**
55743      * Shows this region if it was previously hidden.
55744      */
55745     show : function(){
55746         if(!this.collapsed){
55747             this.el.show();
55748         }else{
55749             this.collapsedEl.show();
55750         }
55751         this.visible = true;
55752         this.fireEvent("visibilitychange", this, true);
55753     },
55754
55755     closeClicked : function(){
55756         if(this.activePanel){
55757             this.remove(this.activePanel);
55758         }
55759     },
55760
55761     collapseClick : function(e){
55762         if(this.isSlid){
55763            e.stopPropagation();
55764            this.slideIn();
55765         }else{
55766            e.stopPropagation();
55767            this.slideOut();
55768         }
55769     },
55770
55771     /**
55772      * Collapses this region.
55773      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55774      */
55775     collapse : function(skipAnim, skipCheck){
55776         if(this.collapsed) {
55777             return;
55778         }
55779         
55780         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55781             
55782             this.collapsed = true;
55783             if(this.split){
55784                 this.split.el.hide();
55785             }
55786             if(this.config.animate && skipAnim !== true){
55787                 this.fireEvent("invalidated", this);
55788                 this.animateCollapse();
55789             }else{
55790                 this.el.setLocation(-20000,-20000);
55791                 this.el.hide();
55792                 this.collapsedEl.show();
55793                 this.fireEvent("collapsed", this);
55794                 this.fireEvent("invalidated", this);
55795             }
55796         }
55797         
55798     },
55799
55800     animateCollapse : function(){
55801         // overridden
55802     },
55803
55804     /**
55805      * Expands this region if it was previously collapsed.
55806      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55807      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55808      */
55809     expand : function(e, skipAnim){
55810         if(e) {
55811             e.stopPropagation();
55812         }
55813         if(!this.collapsed || this.el.hasActiveFx()) {
55814             return;
55815         }
55816         if(this.isSlid){
55817             this.afterSlideIn();
55818             skipAnim = true;
55819         }
55820         this.collapsed = false;
55821         if(this.config.animate && skipAnim !== true){
55822             this.animateExpand();
55823         }else{
55824             this.el.show();
55825             if(this.split){
55826                 this.split.el.show();
55827             }
55828             this.collapsedEl.setLocation(-2000,-2000);
55829             this.collapsedEl.hide();
55830             this.fireEvent("invalidated", this);
55831             this.fireEvent("expanded", this);
55832         }
55833     },
55834
55835     animateExpand : function(){
55836         // overridden
55837     },
55838
55839     initTabs : function()
55840     {
55841         this.bodyEl.setStyle("overflow", "hidden");
55842         var ts = new Roo.TabPanel(
55843                 this.bodyEl.dom,
55844                 {
55845                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55846                     disableTooltips: this.config.disableTabTips,
55847                     toolbar : this.config.toolbar
55848                 }
55849         );
55850         if(this.config.hideTabs){
55851             ts.stripWrap.setDisplayed(false);
55852         }
55853         this.tabs = ts;
55854         ts.resizeTabs = this.config.resizeTabs === true;
55855         ts.minTabWidth = this.config.minTabWidth || 40;
55856         ts.maxTabWidth = this.config.maxTabWidth || 250;
55857         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55858         ts.monitorResize = false;
55859         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55860         ts.bodyEl.addClass('x-layout-tabs-body');
55861         this.panels.each(this.initPanelAsTab, this);
55862     },
55863
55864     initPanelAsTab : function(panel){
55865         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55866                     this.config.closeOnTab && panel.isClosable());
55867         if(panel.tabTip !== undefined){
55868             ti.setTooltip(panel.tabTip);
55869         }
55870         ti.on("activate", function(){
55871               this.setActivePanel(panel);
55872         }, this);
55873         if(this.config.closeOnTab){
55874             ti.on("beforeclose", function(t, e){
55875                 e.cancel = true;
55876                 this.remove(panel);
55877             }, this);
55878         }
55879         return ti;
55880     },
55881
55882     updatePanelTitle : function(panel, title){
55883         if(this.activePanel == panel){
55884             this.updateTitle(title);
55885         }
55886         if(this.tabs){
55887             var ti = this.tabs.getTab(panel.getEl().id);
55888             ti.setText(title);
55889             if(panel.tabTip !== undefined){
55890                 ti.setTooltip(panel.tabTip);
55891             }
55892         }
55893     },
55894
55895     updateTitle : function(title){
55896         if(this.titleTextEl && !this.config.title){
55897             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55898         }
55899     },
55900
55901     setActivePanel : function(panel){
55902         panel = this.getPanel(panel);
55903         if(this.activePanel && this.activePanel != panel){
55904             this.activePanel.setActiveState(false);
55905         }
55906         this.activePanel = panel;
55907         panel.setActiveState(true);
55908         if(this.panelSize){
55909             panel.setSize(this.panelSize.width, this.panelSize.height);
55910         }
55911         if(this.closeBtn){
55912             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55913         }
55914         this.updateTitle(panel.getTitle());
55915         if(this.tabs){
55916             this.fireEvent("invalidated", this);
55917         }
55918         this.fireEvent("panelactivated", this, panel);
55919     },
55920
55921     /**
55922      * Shows the specified panel.
55923      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55924      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55925      */
55926     showPanel : function(panel)
55927     {
55928         panel = this.getPanel(panel);
55929         if(panel){
55930             if(this.tabs){
55931                 var tab = this.tabs.getTab(panel.getEl().id);
55932                 if(tab.isHidden()){
55933                     this.tabs.unhideTab(tab.id);
55934                 }
55935                 tab.activate();
55936             }else{
55937                 this.setActivePanel(panel);
55938             }
55939         }
55940         return panel;
55941     },
55942
55943     /**
55944      * Get the active panel for this region.
55945      * @return {Roo.ContentPanel} The active panel or null
55946      */
55947     getActivePanel : function(){
55948         return this.activePanel;
55949     },
55950
55951     validateVisibility : function(){
55952         if(this.panels.getCount() < 1){
55953             this.updateTitle("&#160;");
55954             this.closeBtn.hide();
55955             this.hide();
55956         }else{
55957             if(!this.isVisible()){
55958                 this.show();
55959             }
55960         }
55961     },
55962
55963     /**
55964      * Adds the passed ContentPanel(s) to this region.
55965      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55966      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55967      */
55968     add : function(panel){
55969         if(arguments.length > 1){
55970             for(var i = 0, len = arguments.length; i < len; i++) {
55971                 this.add(arguments[i]);
55972             }
55973             return null;
55974         }
55975         if(this.hasPanel(panel)){
55976             this.showPanel(panel);
55977             return panel;
55978         }
55979         panel.setRegion(this);
55980         this.panels.add(panel);
55981         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
55982             this.bodyEl.dom.appendChild(panel.getEl().dom);
55983             if(panel.background !== true){
55984                 this.setActivePanel(panel);
55985             }
55986             this.fireEvent("paneladded", this, panel);
55987             return panel;
55988         }
55989         if(!this.tabs){
55990             this.initTabs();
55991         }else{
55992             this.initPanelAsTab(panel);
55993         }
55994         if(panel.background !== true){
55995             this.tabs.activate(panel.getEl().id);
55996         }
55997         this.fireEvent("paneladded", this, panel);
55998         return panel;
55999     },
56000
56001     /**
56002      * Hides the tab for the specified panel.
56003      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56004      */
56005     hidePanel : function(panel){
56006         if(this.tabs && (panel = this.getPanel(panel))){
56007             this.tabs.hideTab(panel.getEl().id);
56008         }
56009     },
56010
56011     /**
56012      * Unhides the tab for a previously hidden panel.
56013      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56014      */
56015     unhidePanel : function(panel){
56016         if(this.tabs && (panel = this.getPanel(panel))){
56017             this.tabs.unhideTab(panel.getEl().id);
56018         }
56019     },
56020
56021     clearPanels : function(){
56022         while(this.panels.getCount() > 0){
56023              this.remove(this.panels.first());
56024         }
56025     },
56026
56027     /**
56028      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56029      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56030      * @param {Boolean} preservePanel Overrides the config preservePanel option
56031      * @return {Roo.ContentPanel} The panel that was removed
56032      */
56033     remove : function(panel, preservePanel){
56034         panel = this.getPanel(panel);
56035         if(!panel){
56036             return null;
56037         }
56038         var e = {};
56039         this.fireEvent("beforeremove", this, panel, e);
56040         if(e.cancel === true){
56041             return null;
56042         }
56043         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56044         var panelId = panel.getId();
56045         this.panels.removeKey(panelId);
56046         if(preservePanel){
56047             document.body.appendChild(panel.getEl().dom);
56048         }
56049         if(this.tabs){
56050             this.tabs.removeTab(panel.getEl().id);
56051         }else if (!preservePanel){
56052             this.bodyEl.dom.removeChild(panel.getEl().dom);
56053         }
56054         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56055             var p = this.panels.first();
56056             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56057             tempEl.appendChild(p.getEl().dom);
56058             this.bodyEl.update("");
56059             this.bodyEl.dom.appendChild(p.getEl().dom);
56060             tempEl = null;
56061             this.updateTitle(p.getTitle());
56062             this.tabs = null;
56063             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56064             this.setActivePanel(p);
56065         }
56066         panel.setRegion(null);
56067         if(this.activePanel == panel){
56068             this.activePanel = null;
56069         }
56070         if(this.config.autoDestroy !== false && preservePanel !== true){
56071             try{panel.destroy();}catch(e){}
56072         }
56073         this.fireEvent("panelremoved", this, panel);
56074         return panel;
56075     },
56076
56077     /**
56078      * Returns the TabPanel component used by this region
56079      * @return {Roo.TabPanel}
56080      */
56081     getTabs : function(){
56082         return this.tabs;
56083     },
56084
56085     createTool : function(parentEl, className){
56086         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56087             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56088         btn.addClassOnOver("x-layout-tools-button-over");
56089         return btn;
56090     }
56091 });/*
56092  * Based on:
56093  * Ext JS Library 1.1.1
56094  * Copyright(c) 2006-2007, Ext JS, LLC.
56095  *
56096  * Originally Released Under LGPL - original licence link has changed is not relivant.
56097  *
56098  * Fork - LGPL
56099  * <script type="text/javascript">
56100  */
56101  
56102
56103
56104 /**
56105  * @class Roo.SplitLayoutRegion
56106  * @extends Roo.LayoutRegion
56107  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56108  */
56109 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56110     this.cursor = cursor;
56111     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56112 };
56113
56114 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56115     splitTip : "Drag to resize.",
56116     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56117     useSplitTips : false,
56118
56119     applyConfig : function(config){
56120         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56121         if(config.split){
56122             if(!this.split){
56123                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56124                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56125                 /** The SplitBar for this region 
56126                 * @type Roo.SplitBar */
56127                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56128                 this.split.on("moved", this.onSplitMove, this);
56129                 this.split.useShim = config.useShim === true;
56130                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56131                 if(this.useSplitTips){
56132                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56133                 }
56134                 if(config.collapsible){
56135                     this.split.el.on("dblclick", this.collapse,  this);
56136                 }
56137             }
56138             if(typeof config.minSize != "undefined"){
56139                 this.split.minSize = config.minSize;
56140             }
56141             if(typeof config.maxSize != "undefined"){
56142                 this.split.maxSize = config.maxSize;
56143             }
56144             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56145                 this.hideSplitter();
56146             }
56147         }
56148     },
56149
56150     getHMaxSize : function(){
56151          var cmax = this.config.maxSize || 10000;
56152          var center = this.mgr.getRegion("center");
56153          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56154     },
56155
56156     getVMaxSize : function(){
56157          var cmax = this.config.maxSize || 10000;
56158          var center = this.mgr.getRegion("center");
56159          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56160     },
56161
56162     onSplitMove : function(split, newSize){
56163         this.fireEvent("resized", this, newSize);
56164     },
56165     
56166     /** 
56167      * Returns the {@link Roo.SplitBar} for this region.
56168      * @return {Roo.SplitBar}
56169      */
56170     getSplitBar : function(){
56171         return this.split;
56172     },
56173     
56174     hide : function(){
56175         this.hideSplitter();
56176         Roo.SplitLayoutRegion.superclass.hide.call(this);
56177     },
56178
56179     hideSplitter : function(){
56180         if(this.split){
56181             this.split.el.setLocation(-2000,-2000);
56182             this.split.el.hide();
56183         }
56184     },
56185
56186     show : function(){
56187         if(this.split){
56188             this.split.el.show();
56189         }
56190         Roo.SplitLayoutRegion.superclass.show.call(this);
56191     },
56192     
56193     beforeSlide: function(){
56194         if(Roo.isGecko){// firefox overflow auto bug workaround
56195             this.bodyEl.clip();
56196             if(this.tabs) {
56197                 this.tabs.bodyEl.clip();
56198             }
56199             if(this.activePanel){
56200                 this.activePanel.getEl().clip();
56201                 
56202                 if(this.activePanel.beforeSlide){
56203                     this.activePanel.beforeSlide();
56204                 }
56205             }
56206         }
56207     },
56208     
56209     afterSlide : function(){
56210         if(Roo.isGecko){// firefox overflow auto bug workaround
56211             this.bodyEl.unclip();
56212             if(this.tabs) {
56213                 this.tabs.bodyEl.unclip();
56214             }
56215             if(this.activePanel){
56216                 this.activePanel.getEl().unclip();
56217                 if(this.activePanel.afterSlide){
56218                     this.activePanel.afterSlide();
56219                 }
56220             }
56221         }
56222     },
56223
56224     initAutoHide : function(){
56225         if(this.autoHide !== false){
56226             if(!this.autoHideHd){
56227                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56228                 this.autoHideHd = {
56229                     "mouseout": function(e){
56230                         if(!e.within(this.el, true)){
56231                             st.delay(500);
56232                         }
56233                     },
56234                     "mouseover" : function(e){
56235                         st.cancel();
56236                     },
56237                     scope : this
56238                 };
56239             }
56240             this.el.on(this.autoHideHd);
56241         }
56242     },
56243
56244     clearAutoHide : function(){
56245         if(this.autoHide !== false){
56246             this.el.un("mouseout", this.autoHideHd.mouseout);
56247             this.el.un("mouseover", this.autoHideHd.mouseover);
56248         }
56249     },
56250
56251     clearMonitor : function(){
56252         Roo.get(document).un("click", this.slideInIf, this);
56253     },
56254
56255     // these names are backwards but not changed for compat
56256     slideOut : function(){
56257         if(this.isSlid || this.el.hasActiveFx()){
56258             return;
56259         }
56260         this.isSlid = true;
56261         if(this.collapseBtn){
56262             this.collapseBtn.hide();
56263         }
56264         this.closeBtnState = this.closeBtn.getStyle('display');
56265         this.closeBtn.hide();
56266         if(this.stickBtn){
56267             this.stickBtn.show();
56268         }
56269         this.el.show();
56270         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56271         this.beforeSlide();
56272         this.el.setStyle("z-index", 10001);
56273         this.el.slideIn(this.getSlideAnchor(), {
56274             callback: function(){
56275                 this.afterSlide();
56276                 this.initAutoHide();
56277                 Roo.get(document).on("click", this.slideInIf, this);
56278                 this.fireEvent("slideshow", this);
56279             },
56280             scope: this,
56281             block: true
56282         });
56283     },
56284
56285     afterSlideIn : function(){
56286         this.clearAutoHide();
56287         this.isSlid = false;
56288         this.clearMonitor();
56289         this.el.setStyle("z-index", "");
56290         if(this.collapseBtn){
56291             this.collapseBtn.show();
56292         }
56293         this.closeBtn.setStyle('display', this.closeBtnState);
56294         if(this.stickBtn){
56295             this.stickBtn.hide();
56296         }
56297         this.fireEvent("slidehide", this);
56298     },
56299
56300     slideIn : function(cb){
56301         if(!this.isSlid || this.el.hasActiveFx()){
56302             Roo.callback(cb);
56303             return;
56304         }
56305         this.isSlid = false;
56306         this.beforeSlide();
56307         this.el.slideOut(this.getSlideAnchor(), {
56308             callback: function(){
56309                 this.el.setLeftTop(-10000, -10000);
56310                 this.afterSlide();
56311                 this.afterSlideIn();
56312                 Roo.callback(cb);
56313             },
56314             scope: this,
56315             block: true
56316         });
56317     },
56318     
56319     slideInIf : function(e){
56320         if(!e.within(this.el)){
56321             this.slideIn();
56322         }
56323     },
56324
56325     animateCollapse : function(){
56326         this.beforeSlide();
56327         this.el.setStyle("z-index", 20000);
56328         var anchor = this.getSlideAnchor();
56329         this.el.slideOut(anchor, {
56330             callback : function(){
56331                 this.el.setStyle("z-index", "");
56332                 this.collapsedEl.slideIn(anchor, {duration:.3});
56333                 this.afterSlide();
56334                 this.el.setLocation(-10000,-10000);
56335                 this.el.hide();
56336                 this.fireEvent("collapsed", this);
56337             },
56338             scope: this,
56339             block: true
56340         });
56341     },
56342
56343     animateExpand : function(){
56344         this.beforeSlide();
56345         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56346         this.el.setStyle("z-index", 20000);
56347         this.collapsedEl.hide({
56348             duration:.1
56349         });
56350         this.el.slideIn(this.getSlideAnchor(), {
56351             callback : function(){
56352                 this.el.setStyle("z-index", "");
56353                 this.afterSlide();
56354                 if(this.split){
56355                     this.split.el.show();
56356                 }
56357                 this.fireEvent("invalidated", this);
56358                 this.fireEvent("expanded", this);
56359             },
56360             scope: this,
56361             block: true
56362         });
56363     },
56364
56365     anchors : {
56366         "west" : "left",
56367         "east" : "right",
56368         "north" : "top",
56369         "south" : "bottom"
56370     },
56371
56372     sanchors : {
56373         "west" : "l",
56374         "east" : "r",
56375         "north" : "t",
56376         "south" : "b"
56377     },
56378
56379     canchors : {
56380         "west" : "tl-tr",
56381         "east" : "tr-tl",
56382         "north" : "tl-bl",
56383         "south" : "bl-tl"
56384     },
56385
56386     getAnchor : function(){
56387         return this.anchors[this.position];
56388     },
56389
56390     getCollapseAnchor : function(){
56391         return this.canchors[this.position];
56392     },
56393
56394     getSlideAnchor : function(){
56395         return this.sanchors[this.position];
56396     },
56397
56398     getAlignAdj : function(){
56399         var cm = this.cmargins;
56400         switch(this.position){
56401             case "west":
56402                 return [0, 0];
56403             break;
56404             case "east":
56405                 return [0, 0];
56406             break;
56407             case "north":
56408                 return [0, 0];
56409             break;
56410             case "south":
56411                 return [0, 0];
56412             break;
56413         }
56414     },
56415
56416     getExpandAdj : function(){
56417         var c = this.collapsedEl, cm = this.cmargins;
56418         switch(this.position){
56419             case "west":
56420                 return [-(cm.right+c.getWidth()+cm.left), 0];
56421             break;
56422             case "east":
56423                 return [cm.right+c.getWidth()+cm.left, 0];
56424             break;
56425             case "north":
56426                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56427             break;
56428             case "south":
56429                 return [0, cm.top+cm.bottom+c.getHeight()];
56430             break;
56431         }
56432     }
56433 });/*
56434  * Based on:
56435  * Ext JS Library 1.1.1
56436  * Copyright(c) 2006-2007, Ext JS, LLC.
56437  *
56438  * Originally Released Under LGPL - original licence link has changed is not relivant.
56439  *
56440  * Fork - LGPL
56441  * <script type="text/javascript">
56442  */
56443 /*
56444  * These classes are private internal classes
56445  */
56446 Roo.CenterLayoutRegion = function(mgr, config){
56447     Roo.LayoutRegion.call(this, mgr, config, "center");
56448     this.visible = true;
56449     this.minWidth = config.minWidth || 20;
56450     this.minHeight = config.minHeight || 20;
56451 };
56452
56453 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56454     hide : function(){
56455         // center panel can't be hidden
56456     },
56457     
56458     show : function(){
56459         // center panel can't be hidden
56460     },
56461     
56462     getMinWidth: function(){
56463         return this.minWidth;
56464     },
56465     
56466     getMinHeight: function(){
56467         return this.minHeight;
56468     }
56469 });
56470
56471
56472 Roo.NorthLayoutRegion = function(mgr, config){
56473     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56474     if(this.split){
56475         this.split.placement = Roo.SplitBar.TOP;
56476         this.split.orientation = Roo.SplitBar.VERTICAL;
56477         this.split.el.addClass("x-layout-split-v");
56478     }
56479     var size = config.initialSize || config.height;
56480     if(typeof size != "undefined"){
56481         this.el.setHeight(size);
56482     }
56483 };
56484 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56485     orientation: Roo.SplitBar.VERTICAL,
56486     getBox : function(){
56487         if(this.collapsed){
56488             return this.collapsedEl.getBox();
56489         }
56490         var box = this.el.getBox();
56491         if(this.split){
56492             box.height += this.split.el.getHeight();
56493         }
56494         return box;
56495     },
56496     
56497     updateBox : function(box){
56498         if(this.split && !this.collapsed){
56499             box.height -= this.split.el.getHeight();
56500             this.split.el.setLeft(box.x);
56501             this.split.el.setTop(box.y+box.height);
56502             this.split.el.setWidth(box.width);
56503         }
56504         if(this.collapsed){
56505             this.updateBody(box.width, null);
56506         }
56507         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56508     }
56509 });
56510
56511 Roo.SouthLayoutRegion = function(mgr, config){
56512     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56513     if(this.split){
56514         this.split.placement = Roo.SplitBar.BOTTOM;
56515         this.split.orientation = Roo.SplitBar.VERTICAL;
56516         this.split.el.addClass("x-layout-split-v");
56517     }
56518     var size = config.initialSize || config.height;
56519     if(typeof size != "undefined"){
56520         this.el.setHeight(size);
56521     }
56522 };
56523 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56524     orientation: Roo.SplitBar.VERTICAL,
56525     getBox : function(){
56526         if(this.collapsed){
56527             return this.collapsedEl.getBox();
56528         }
56529         var box = this.el.getBox();
56530         if(this.split){
56531             var sh = this.split.el.getHeight();
56532             box.height += sh;
56533             box.y -= sh;
56534         }
56535         return box;
56536     },
56537     
56538     updateBox : function(box){
56539         if(this.split && !this.collapsed){
56540             var sh = this.split.el.getHeight();
56541             box.height -= sh;
56542             box.y += sh;
56543             this.split.el.setLeft(box.x);
56544             this.split.el.setTop(box.y-sh);
56545             this.split.el.setWidth(box.width);
56546         }
56547         if(this.collapsed){
56548             this.updateBody(box.width, null);
56549         }
56550         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56551     }
56552 });
56553
56554 Roo.EastLayoutRegion = function(mgr, config){
56555     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56556     if(this.split){
56557         this.split.placement = Roo.SplitBar.RIGHT;
56558         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56559         this.split.el.addClass("x-layout-split-h");
56560     }
56561     var size = config.initialSize || config.width;
56562     if(typeof size != "undefined"){
56563         this.el.setWidth(size);
56564     }
56565 };
56566 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56567     orientation: Roo.SplitBar.HORIZONTAL,
56568     getBox : function(){
56569         if(this.collapsed){
56570             return this.collapsedEl.getBox();
56571         }
56572         var box = this.el.getBox();
56573         if(this.split){
56574             var sw = this.split.el.getWidth();
56575             box.width += sw;
56576             box.x -= sw;
56577         }
56578         return box;
56579     },
56580
56581     updateBox : function(box){
56582         if(this.split && !this.collapsed){
56583             var sw = this.split.el.getWidth();
56584             box.width -= sw;
56585             this.split.el.setLeft(box.x);
56586             this.split.el.setTop(box.y);
56587             this.split.el.setHeight(box.height);
56588             box.x += sw;
56589         }
56590         if(this.collapsed){
56591             this.updateBody(null, box.height);
56592         }
56593         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56594     }
56595 });
56596
56597 Roo.WestLayoutRegion = function(mgr, config){
56598     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56599     if(this.split){
56600         this.split.placement = Roo.SplitBar.LEFT;
56601         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56602         this.split.el.addClass("x-layout-split-h");
56603     }
56604     var size = config.initialSize || config.width;
56605     if(typeof size != "undefined"){
56606         this.el.setWidth(size);
56607     }
56608 };
56609 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56610     orientation: Roo.SplitBar.HORIZONTAL,
56611     getBox : function(){
56612         if(this.collapsed){
56613             return this.collapsedEl.getBox();
56614         }
56615         var box = this.el.getBox();
56616         if(this.split){
56617             box.width += this.split.el.getWidth();
56618         }
56619         return box;
56620     },
56621     
56622     updateBox : function(box){
56623         if(this.split && !this.collapsed){
56624             var sw = this.split.el.getWidth();
56625             box.width -= sw;
56626             this.split.el.setLeft(box.x+box.width);
56627             this.split.el.setTop(box.y);
56628             this.split.el.setHeight(box.height);
56629         }
56630         if(this.collapsed){
56631             this.updateBody(null, box.height);
56632         }
56633         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56634     }
56635 });
56636 /*
56637  * Based on:
56638  * Ext JS Library 1.1.1
56639  * Copyright(c) 2006-2007, Ext JS, LLC.
56640  *
56641  * Originally Released Under LGPL - original licence link has changed is not relivant.
56642  *
56643  * Fork - LGPL
56644  * <script type="text/javascript">
56645  */
56646  
56647  
56648 /*
56649  * Private internal class for reading and applying state
56650  */
56651 Roo.LayoutStateManager = function(layout){
56652      // default empty state
56653      this.state = {
56654         north: {},
56655         south: {},
56656         east: {},
56657         west: {}       
56658     };
56659 };
56660
56661 Roo.LayoutStateManager.prototype = {
56662     init : function(layout, provider){
56663         this.provider = provider;
56664         var state = provider.get(layout.id+"-layout-state");
56665         if(state){
56666             var wasUpdating = layout.isUpdating();
56667             if(!wasUpdating){
56668                 layout.beginUpdate();
56669             }
56670             for(var key in state){
56671                 if(typeof state[key] != "function"){
56672                     var rstate = state[key];
56673                     var r = layout.getRegion(key);
56674                     if(r && rstate){
56675                         if(rstate.size){
56676                             r.resizeTo(rstate.size);
56677                         }
56678                         if(rstate.collapsed == true){
56679                             r.collapse(true);
56680                         }else{
56681                             r.expand(null, true);
56682                         }
56683                     }
56684                 }
56685             }
56686             if(!wasUpdating){
56687                 layout.endUpdate();
56688             }
56689             this.state = state; 
56690         }
56691         this.layout = layout;
56692         layout.on("regionresized", this.onRegionResized, this);
56693         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56694         layout.on("regionexpanded", this.onRegionExpanded, this);
56695     },
56696     
56697     storeState : function(){
56698         this.provider.set(this.layout.id+"-layout-state", this.state);
56699     },
56700     
56701     onRegionResized : function(region, newSize){
56702         this.state[region.getPosition()].size = newSize;
56703         this.storeState();
56704     },
56705     
56706     onRegionCollapsed : function(region){
56707         this.state[region.getPosition()].collapsed = true;
56708         this.storeState();
56709     },
56710     
56711     onRegionExpanded : function(region){
56712         this.state[region.getPosition()].collapsed = false;
56713         this.storeState();
56714     }
56715 };/*
56716  * Based on:
56717  * Ext JS Library 1.1.1
56718  * Copyright(c) 2006-2007, Ext JS, LLC.
56719  *
56720  * Originally Released Under LGPL - original licence link has changed is not relivant.
56721  *
56722  * Fork - LGPL
56723  * <script type="text/javascript">
56724  */
56725 /**
56726  * @class Roo.ContentPanel
56727  * @extends Roo.util.Observable
56728  * @children Roo.form.Form Roo.JsonView Roo.View
56729  * @parent Roo.BorderLayout Roo.LayoutDialog builder
56730  * A basic ContentPanel element.
56731  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56732  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56733  * @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
56734  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56735  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56736  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56737  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56738  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56739  * @cfg {String} title          The title for this panel
56740  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56741  * @cfg {String} url            Calls {@link #setUrl} with this value
56742  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
56743  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56744  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56745  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56746  * @cfg {String}    style  Extra style to add to the content panel
56747  * @cfg {Roo.menu.Menu} menu  popup menu
56748
56749  * @constructor
56750  * Create a new ContentPanel.
56751  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56752  * @param {String/Object} config A string to set only the title or a config object
56753  * @param {String} content (optional) Set the HTML content for this panel
56754  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56755  */
56756 Roo.ContentPanel = function(el, config, content){
56757     
56758      
56759     /*
56760     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56761         config = el;
56762         el = Roo.id();
56763     }
56764     if (config && config.parentLayout) { 
56765         el = config.parentLayout.el.createChild(); 
56766     }
56767     */
56768     if(el.autoCreate){ // xtype is available if this is called from factory
56769         config = el;
56770         el = Roo.id();
56771     }
56772     this.el = Roo.get(el);
56773     if(!this.el && config && config.autoCreate){
56774         if(typeof config.autoCreate == "object"){
56775             if(!config.autoCreate.id){
56776                 config.autoCreate.id = config.id||el;
56777             }
56778             this.el = Roo.DomHelper.append(document.body,
56779                         config.autoCreate, true);
56780         }else{
56781             this.el = Roo.DomHelper.append(document.body,
56782                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56783         }
56784     }
56785     
56786     
56787     this.closable = false;
56788     this.loaded = false;
56789     this.active = false;
56790     if(typeof config == "string"){
56791         this.title = config;
56792     }else{
56793         Roo.apply(this, config);
56794     }
56795     
56796     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56797         this.wrapEl = this.el.wrap();
56798         this.toolbar.container = this.el.insertSibling(false, 'before');
56799         this.toolbar = new Roo.Toolbar(this.toolbar);
56800     }
56801     
56802     // xtype created footer. - not sure if will work as we normally have to render first..
56803     if (this.footer && !this.footer.el && this.footer.xtype) {
56804         if (!this.wrapEl) {
56805             this.wrapEl = this.el.wrap();
56806         }
56807     
56808         this.footer.container = this.wrapEl.createChild();
56809          
56810         this.footer = Roo.factory(this.footer, Roo);
56811         
56812     }
56813     
56814     if(this.resizeEl){
56815         this.resizeEl = Roo.get(this.resizeEl, true);
56816     }else{
56817         this.resizeEl = this.el;
56818     }
56819     // handle view.xtype
56820     
56821  
56822     
56823     
56824     this.addEvents({
56825         /**
56826          * @event activate
56827          * Fires when this panel is activated. 
56828          * @param {Roo.ContentPanel} this
56829          */
56830         "activate" : true,
56831         /**
56832          * @event deactivate
56833          * Fires when this panel is activated. 
56834          * @param {Roo.ContentPanel} this
56835          */
56836         "deactivate" : true,
56837
56838         /**
56839          * @event resize
56840          * Fires when this panel is resized if fitToFrame is true.
56841          * @param {Roo.ContentPanel} this
56842          * @param {Number} width The width after any component adjustments
56843          * @param {Number} height The height after any component adjustments
56844          */
56845         "resize" : true,
56846         
56847          /**
56848          * @event render
56849          * Fires when this tab is created
56850          * @param {Roo.ContentPanel} this
56851          */
56852         "render" : true
56853          
56854         
56855     });
56856     
56857
56858     
56859     
56860     if(this.autoScroll){
56861         this.resizeEl.setStyle("overflow", "auto");
56862     } else {
56863         // fix randome scrolling
56864         this.el.on('scroll', function() {
56865             Roo.log('fix random scolling');
56866             this.scrollTo('top',0); 
56867         });
56868     }
56869     content = content || this.content;
56870     if(content){
56871         this.setContent(content);
56872     }
56873     if(config && config.url){
56874         this.setUrl(this.url, this.params, this.loadOnce);
56875     }
56876     
56877     
56878     
56879     Roo.ContentPanel.superclass.constructor.call(this);
56880     
56881     if (this.view && typeof(this.view.xtype) != 'undefined') {
56882         this.view.el = this.el.appendChild(document.createElement("div"));
56883         this.view = Roo.factory(this.view); 
56884         this.view.render  &&  this.view.render(false, '');  
56885     }
56886     
56887     
56888     this.fireEvent('render', this);
56889 };
56890
56891 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56892     tabTip:'',
56893     setRegion : function(region){
56894         this.region = region;
56895         if(region){
56896            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56897         }else{
56898            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56899         } 
56900     },
56901     
56902     /**
56903      * Returns the toolbar for this Panel if one was configured. 
56904      * @return {Roo.Toolbar} 
56905      */
56906     getToolbar : function(){
56907         return this.toolbar;
56908     },
56909     
56910     setActiveState : function(active){
56911         this.active = active;
56912         if(!active){
56913             this.fireEvent("deactivate", this);
56914         }else{
56915             this.fireEvent("activate", this);
56916         }
56917     },
56918     /**
56919      * Updates this panel's element
56920      * @param {String} content The new content
56921      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56922     */
56923     setContent : function(content, loadScripts){
56924         this.el.update(content, loadScripts);
56925     },
56926
56927     ignoreResize : function(w, h){
56928         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56929             return true;
56930         }else{
56931             this.lastSize = {width: w, height: h};
56932             return false;
56933         }
56934     },
56935     /**
56936      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56937      * @return {Roo.UpdateManager} The UpdateManager
56938      */
56939     getUpdateManager : function(){
56940         return this.el.getUpdateManager();
56941     },
56942      /**
56943      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56944      * @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:
56945 <pre><code>
56946 panel.load({
56947     url: "your-url.php",
56948     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56949     callback: yourFunction,
56950     scope: yourObject, //(optional scope)
56951     discardUrl: false,
56952     nocache: false,
56953     text: "Loading...",
56954     timeout: 30,
56955     scripts: false
56956 });
56957 </code></pre>
56958      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56959      * 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.
56960      * @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}
56961      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56962      * @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.
56963      * @return {Roo.ContentPanel} this
56964      */
56965     load : function(){
56966         var um = this.el.getUpdateManager();
56967         um.update.apply(um, arguments);
56968         return this;
56969     },
56970
56971
56972     /**
56973      * 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.
56974      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56975      * @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)
56976      * @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)
56977      * @return {Roo.UpdateManager} The UpdateManager
56978      */
56979     setUrl : function(url, params, loadOnce){
56980         if(this.refreshDelegate){
56981             this.removeListener("activate", this.refreshDelegate);
56982         }
56983         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
56984         this.on("activate", this.refreshDelegate);
56985         return this.el.getUpdateManager();
56986     },
56987     
56988     _handleRefresh : function(url, params, loadOnce){
56989         if(!loadOnce || !this.loaded){
56990             var updater = this.el.getUpdateManager();
56991             updater.update(url, params, this._setLoaded.createDelegate(this));
56992         }
56993     },
56994     
56995     _setLoaded : function(){
56996         this.loaded = true;
56997     }, 
56998     
56999     /**
57000      * Returns this panel's id
57001      * @return {String} 
57002      */
57003     getId : function(){
57004         return this.el.id;
57005     },
57006     
57007     /** 
57008      * Returns this panel's element - used by regiosn to add.
57009      * @return {Roo.Element} 
57010      */
57011     getEl : function(){
57012         return this.wrapEl || this.el;
57013     },
57014     
57015     adjustForComponents : function(width, height)
57016     {
57017         //Roo.log('adjustForComponents ');
57018         if(this.resizeEl != this.el){
57019             width -= this.el.getFrameWidth('lr');
57020             height -= this.el.getFrameWidth('tb');
57021         }
57022         if(this.toolbar){
57023             var te = this.toolbar.getEl();
57024             height -= te.getHeight();
57025             te.setWidth(width);
57026         }
57027         if(this.footer){
57028             var te = this.footer.getEl();
57029             //Roo.log("footer:" + te.getHeight());
57030             
57031             height -= te.getHeight();
57032             te.setWidth(width);
57033         }
57034         
57035         
57036         if(this.adjustments){
57037             width += this.adjustments[0];
57038             height += this.adjustments[1];
57039         }
57040         return {"width": width, "height": height};
57041     },
57042     
57043     setSize : function(width, height){
57044         if(this.fitToFrame && !this.ignoreResize(width, height)){
57045             if(this.fitContainer && this.resizeEl != this.el){
57046                 this.el.setSize(width, height);
57047             }
57048             var size = this.adjustForComponents(width, height);
57049             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57050             this.fireEvent('resize', this, size.width, size.height);
57051         }
57052     },
57053     
57054     /**
57055      * Returns this panel's title
57056      * @return {String} 
57057      */
57058     getTitle : function(){
57059         return this.title;
57060     },
57061     
57062     /**
57063      * Set this panel's title
57064      * @param {String} title
57065      */
57066     setTitle : function(title){
57067         this.title = title;
57068         if(this.region){
57069             this.region.updatePanelTitle(this, title);
57070         }
57071     },
57072     
57073     /**
57074      * Returns true is this panel was configured to be closable
57075      * @return {Boolean} 
57076      */
57077     isClosable : function(){
57078         return this.closable;
57079     },
57080     
57081     beforeSlide : function(){
57082         this.el.clip();
57083         this.resizeEl.clip();
57084     },
57085     
57086     afterSlide : function(){
57087         this.el.unclip();
57088         this.resizeEl.unclip();
57089     },
57090     
57091     /**
57092      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57093      *   Will fail silently if the {@link #setUrl} method has not been called.
57094      *   This does not activate the panel, just updates its content.
57095      */
57096     refresh : function(){
57097         if(this.refreshDelegate){
57098            this.loaded = false;
57099            this.refreshDelegate();
57100         }
57101     },
57102     
57103     /**
57104      * Destroys this panel
57105      */
57106     destroy : function(){
57107         this.el.removeAllListeners();
57108         var tempEl = document.createElement("span");
57109         tempEl.appendChild(this.el.dom);
57110         tempEl.innerHTML = "";
57111         this.el.remove();
57112         this.el = null;
57113     },
57114     
57115     /**
57116      * form - if the content panel contains a form - this is a reference to it.
57117      * @type {Roo.form.Form}
57118      */
57119     form : false,
57120     /**
57121      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57122      *    This contains a reference to it.
57123      * @type {Roo.View}
57124      */
57125     view : false,
57126     
57127       /**
57128      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57129      * <pre><code>
57130
57131 layout.addxtype({
57132        xtype : 'Form',
57133        items: [ .... ]
57134    }
57135 );
57136
57137 </code></pre>
57138      * @param {Object} cfg Xtype definition of item to add.
57139      */
57140     
57141     addxtype : function(cfg) {
57142         // add form..
57143         if (cfg.xtype.match(/^Form$/)) {
57144             
57145             var el;
57146             //if (this.footer) {
57147             //    el = this.footer.container.insertSibling(false, 'before');
57148             //} else {
57149                 el = this.el.createChild();
57150             //}
57151
57152             this.form = new  Roo.form.Form(cfg);
57153             
57154             
57155             if ( this.form.allItems.length) {
57156                 this.form.render(el.dom);
57157             }
57158             return this.form;
57159         }
57160         // should only have one of theses..
57161         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57162             // views.. should not be just added - used named prop 'view''
57163             
57164             cfg.el = this.el.appendChild(document.createElement("div"));
57165             // factory?
57166             
57167             var ret = new Roo.factory(cfg);
57168              
57169              ret.render && ret.render(false, ''); // render blank..
57170             this.view = ret;
57171             return ret;
57172         }
57173         return false;
57174     }
57175 });
57176
57177 /**
57178  * @class Roo.GridPanel
57179  * @extends Roo.ContentPanel
57180  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57181  * @constructor
57182  * Create a new GridPanel.
57183  * @cfg {Roo.grid.Grid} grid The grid for this panel
57184  */
57185 Roo.GridPanel = function(grid, config){
57186     
57187     // universal ctor...
57188     if (typeof(grid.grid) != 'undefined') {
57189         config = grid;
57190         grid = config.grid;
57191     }
57192     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57193         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57194         
57195     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57196     
57197     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57198     
57199     if(this.toolbar){
57200         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57201     }
57202     // xtype created footer. - not sure if will work as we normally have to render first..
57203     if (this.footer && !this.footer.el && this.footer.xtype) {
57204         
57205         this.footer.container = this.grid.getView().getFooterPanel(true);
57206         this.footer.dataSource = this.grid.dataSource;
57207         this.footer = Roo.factory(this.footer, Roo);
57208         
57209     }
57210     
57211     grid.monitorWindowResize = false; // turn off autosizing
57212     grid.autoHeight = false;
57213     grid.autoWidth = false;
57214     this.grid = grid;
57215     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57216 };
57217
57218 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57219     getId : function(){
57220         return this.grid.id;
57221     },
57222     
57223     /**
57224      * Returns the grid for this panel
57225      * @return {Roo.grid.Grid} 
57226      */
57227     getGrid : function(){
57228         return this.grid;    
57229     },
57230     
57231     setSize : function(width, height){
57232         if(!this.ignoreResize(width, height)){
57233             var grid = this.grid;
57234             var size = this.adjustForComponents(width, height);
57235             grid.getGridEl().setSize(size.width, size.height);
57236             grid.autoSize();
57237         }
57238     },
57239     
57240     beforeSlide : function(){
57241         this.grid.getView().scroller.clip();
57242     },
57243     
57244     afterSlide : function(){
57245         this.grid.getView().scroller.unclip();
57246     },
57247     
57248     destroy : function(){
57249         this.grid.destroy();
57250         delete this.grid;
57251         Roo.GridPanel.superclass.destroy.call(this); 
57252     }
57253 });
57254
57255
57256 /**
57257  * @class Roo.NestedLayoutPanel
57258  * @extends Roo.ContentPanel
57259  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57260  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57261  *
57262  * 
57263  * @constructor
57264  * Create a new NestedLayoutPanel.
57265  * 
57266  * 
57267  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57268  * @param {String/Object} config A string to set only the title or a config object
57269  */
57270 Roo.NestedLayoutPanel = function(layout, config)
57271 {
57272     // construct with only one argument..
57273     /* FIXME - implement nicer consturctors
57274     if (layout.layout) {
57275         config = layout;
57276         layout = config.layout;
57277         delete config.layout;
57278     }
57279     if (layout.xtype && !layout.getEl) {
57280         // then layout needs constructing..
57281         layout = Roo.factory(layout, Roo);
57282     }
57283     */
57284     
57285     
57286     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57287     
57288     layout.monitorWindowResize = false; // turn off autosizing
57289     this.layout = layout;
57290     this.layout.getEl().addClass("x-layout-nested-layout");
57291     
57292     
57293     
57294     
57295 };
57296
57297 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57298
57299     setSize : function(width, height){
57300         if(!this.ignoreResize(width, height)){
57301             var size = this.adjustForComponents(width, height);
57302             var el = this.layout.getEl();
57303             el.setSize(size.width, size.height);
57304             var touch = el.dom.offsetWidth;
57305             this.layout.layout();
57306             // ie requires a double layout on the first pass
57307             if(Roo.isIE && !this.initialized){
57308                 this.initialized = true;
57309                 this.layout.layout();
57310             }
57311         }
57312     },
57313     
57314     // activate all subpanels if not currently active..
57315     
57316     setActiveState : function(active){
57317         this.active = active;
57318         if(!active){
57319             this.fireEvent("deactivate", this);
57320             return;
57321         }
57322         
57323         this.fireEvent("activate", this);
57324         // not sure if this should happen before or after..
57325         if (!this.layout) {
57326             return; // should not happen..
57327         }
57328         var reg = false;
57329         for (var r in this.layout.regions) {
57330             reg = this.layout.getRegion(r);
57331             if (reg.getActivePanel()) {
57332                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57333                 reg.setActivePanel(reg.getActivePanel());
57334                 continue;
57335             }
57336             if (!reg.panels.length) {
57337                 continue;
57338             }
57339             reg.showPanel(reg.getPanel(0));
57340         }
57341         
57342         
57343         
57344         
57345     },
57346     
57347     /**
57348      * Returns the nested BorderLayout for this panel
57349      * @return {Roo.BorderLayout} 
57350      */
57351     getLayout : function(){
57352         return this.layout;
57353     },
57354     
57355      /**
57356      * Adds a xtype elements to the layout of the nested panel
57357      * <pre><code>
57358
57359 panel.addxtype({
57360        xtype : 'ContentPanel',
57361        region: 'west',
57362        items: [ .... ]
57363    }
57364 );
57365
57366 panel.addxtype({
57367         xtype : 'NestedLayoutPanel',
57368         region: 'west',
57369         layout: {
57370            center: { },
57371            west: { }   
57372         },
57373         items : [ ... list of content panels or nested layout panels.. ]
57374    }
57375 );
57376 </code></pre>
57377      * @param {Object} cfg Xtype definition of item to add.
57378      */
57379     addxtype : function(cfg) {
57380         return this.layout.addxtype(cfg);
57381     
57382     }
57383 });
57384
57385 Roo.ScrollPanel = function(el, config, content){
57386     config = config || {};
57387     config.fitToFrame = true;
57388     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57389     
57390     this.el.dom.style.overflow = "hidden";
57391     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57392     this.el.removeClass("x-layout-inactive-content");
57393     this.el.on("mousewheel", this.onWheel, this);
57394
57395     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57396     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57397     up.unselectable(); down.unselectable();
57398     up.on("click", this.scrollUp, this);
57399     down.on("click", this.scrollDown, this);
57400     up.addClassOnOver("x-scroller-btn-over");
57401     down.addClassOnOver("x-scroller-btn-over");
57402     up.addClassOnClick("x-scroller-btn-click");
57403     down.addClassOnClick("x-scroller-btn-click");
57404     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57405
57406     this.resizeEl = this.el;
57407     this.el = wrap; this.up = up; this.down = down;
57408 };
57409
57410 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57411     increment : 100,
57412     wheelIncrement : 5,
57413     scrollUp : function(){
57414         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57415     },
57416
57417     scrollDown : function(){
57418         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57419     },
57420
57421     afterScroll : function(){
57422         var el = this.resizeEl;
57423         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57424         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57425         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57426     },
57427
57428     setSize : function(){
57429         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57430         this.afterScroll();
57431     },
57432
57433     onWheel : function(e){
57434         var d = e.getWheelDelta();
57435         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57436         this.afterScroll();
57437         e.stopEvent();
57438     },
57439
57440     setContent : function(content, loadScripts){
57441         this.resizeEl.update(content, loadScripts);
57442     }
57443
57444 });
57445
57446
57447
57448 /**
57449  * @class Roo.TreePanel
57450  * @extends Roo.ContentPanel
57451  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57452  * Treepanel component
57453  * 
57454  * @constructor
57455  * Create a new TreePanel. - defaults to fit/scoll contents.
57456  * @param {String/Object} config A string to set only the panel's title, or a config object
57457  */
57458 Roo.TreePanel = function(config){
57459     var el = config.el;
57460     var tree = config.tree;
57461     delete config.tree; 
57462     delete config.el; // hopefull!
57463     
57464     // wrapper for IE7 strict & safari scroll issue
57465     
57466     var treeEl = el.createChild();
57467     config.resizeEl = treeEl;
57468     
57469     
57470     
57471     Roo.TreePanel.superclass.constructor.call(this, el, config);
57472  
57473  
57474     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57475     //console.log(tree);
57476     this.on('activate', function()
57477     {
57478         if (this.tree.rendered) {
57479             return;
57480         }
57481         //console.log('render tree');
57482         this.tree.render();
57483     });
57484     // this should not be needed.. - it's actually the 'el' that resizes?
57485     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57486     
57487     //this.on('resize',  function (cp, w, h) {
57488     //        this.tree.innerCt.setWidth(w);
57489     //        this.tree.innerCt.setHeight(h);
57490     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57491     //});
57492
57493         
57494     
57495 };
57496
57497 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57498     fitToFrame : true,
57499     autoScroll : true,
57500     /*
57501      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57502      */
57503     tree : false
57504
57505 });
57506
57507
57508
57509
57510
57511
57512
57513
57514
57515
57516
57517 /*
57518  * Based on:
57519  * Ext JS Library 1.1.1
57520  * Copyright(c) 2006-2007, Ext JS, LLC.
57521  *
57522  * Originally Released Under LGPL - original licence link has changed is not relivant.
57523  *
57524  * Fork - LGPL
57525  * <script type="text/javascript">
57526  */
57527  
57528
57529 /**
57530  * @class Roo.ReaderLayout
57531  * @extends Roo.BorderLayout
57532  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57533  * center region containing two nested regions (a top one for a list view and one for item preview below),
57534  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57535  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57536  * expedites the setup of the overall layout and regions for this common application style.
57537  * Example:
57538  <pre><code>
57539 var reader = new Roo.ReaderLayout();
57540 var CP = Roo.ContentPanel;  // shortcut for adding
57541
57542 reader.beginUpdate();
57543 reader.add("north", new CP("north", "North"));
57544 reader.add("west", new CP("west", {title: "West"}));
57545 reader.add("east", new CP("east", {title: "East"}));
57546
57547 reader.regions.listView.add(new CP("listView", "List"));
57548 reader.regions.preview.add(new CP("preview", "Preview"));
57549 reader.endUpdate();
57550 </code></pre>
57551 * @constructor
57552 * Create a new ReaderLayout
57553 * @param {Object} config Configuration options
57554 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57555 * document.body if omitted)
57556 */
57557 Roo.ReaderLayout = function(config, renderTo){
57558     var c = config || {size:{}};
57559     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57560         north: c.north !== false ? Roo.apply({
57561             split:false,
57562             initialSize: 32,
57563             titlebar: false
57564         }, c.north) : false,
57565         west: c.west !== false ? Roo.apply({
57566             split:true,
57567             initialSize: 200,
57568             minSize: 175,
57569             maxSize: 400,
57570             titlebar: true,
57571             collapsible: true,
57572             animate: true,
57573             margins:{left:5,right:0,bottom:5,top:5},
57574             cmargins:{left:5,right:5,bottom:5,top:5}
57575         }, c.west) : false,
57576         east: c.east !== false ? Roo.apply({
57577             split:true,
57578             initialSize: 200,
57579             minSize: 175,
57580             maxSize: 400,
57581             titlebar: true,
57582             collapsible: true,
57583             animate: true,
57584             margins:{left:0,right:5,bottom:5,top:5},
57585             cmargins:{left:5,right:5,bottom:5,top:5}
57586         }, c.east) : false,
57587         center: Roo.apply({
57588             tabPosition: 'top',
57589             autoScroll:false,
57590             closeOnTab: true,
57591             titlebar:false,
57592             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57593         }, c.center)
57594     });
57595
57596     this.el.addClass('x-reader');
57597
57598     this.beginUpdate();
57599
57600     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57601         south: c.preview !== false ? Roo.apply({
57602             split:true,
57603             initialSize: 200,
57604             minSize: 100,
57605             autoScroll:true,
57606             collapsible:true,
57607             titlebar: true,
57608             cmargins:{top:5,left:0, right:0, bottom:0}
57609         }, c.preview) : false,
57610         center: Roo.apply({
57611             autoScroll:false,
57612             titlebar:false,
57613             minHeight:200
57614         }, c.listView)
57615     });
57616     this.add('center', new Roo.NestedLayoutPanel(inner,
57617             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57618
57619     this.endUpdate();
57620
57621     this.regions.preview = inner.getRegion('south');
57622     this.regions.listView = inner.getRegion('center');
57623 };
57624
57625 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57626  * Based on:
57627  * Ext JS Library 1.1.1
57628  * Copyright(c) 2006-2007, Ext JS, LLC.
57629  *
57630  * Originally Released Under LGPL - original licence link has changed is not relivant.
57631  *
57632  * Fork - LGPL
57633  * <script type="text/javascript">
57634  */
57635  
57636 /**
57637  * @class Roo.grid.Grid
57638  * @extends Roo.util.Observable
57639  * This class represents the primary interface of a component based grid control.
57640  * <br><br>Usage:<pre><code>
57641  var grid = new Roo.grid.Grid("my-container-id", {
57642      ds: myDataStore,
57643      cm: myColModel,
57644      selModel: mySelectionModel,
57645      autoSizeColumns: true,
57646      monitorWindowResize: false,
57647      trackMouseOver: true
57648  });
57649  // set any options
57650  grid.render();
57651  * </code></pre>
57652  * <b>Common Problems:</b><br/>
57653  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57654  * element will correct this<br/>
57655  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57656  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57657  * are unpredictable.<br/>
57658  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57659  * grid to calculate dimensions/offsets.<br/>
57660   * @constructor
57661  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57662  * The container MUST have some type of size defined for the grid to fill. The container will be
57663  * automatically set to position relative if it isn't already.
57664  * @param {Object} config A config object that sets properties on this grid.
57665  */
57666 Roo.grid.Grid = function(container, config){
57667         // initialize the container
57668         this.container = Roo.get(container);
57669         this.container.update("");
57670         this.container.setStyle("overflow", "hidden");
57671     this.container.addClass('x-grid-container');
57672
57673     this.id = this.container.id;
57674
57675     Roo.apply(this, config);
57676     // check and correct shorthanded configs
57677     if(this.ds){
57678         this.dataSource = this.ds;
57679         delete this.ds;
57680     }
57681     if(this.cm){
57682         this.colModel = this.cm;
57683         delete this.cm;
57684     }
57685     if(this.sm){
57686         this.selModel = this.sm;
57687         delete this.sm;
57688     }
57689
57690     if (this.selModel) {
57691         this.selModel = Roo.factory(this.selModel, Roo.grid);
57692         this.sm = this.selModel;
57693         this.sm.xmodule = this.xmodule || false;
57694     }
57695     if (typeof(this.colModel.config) == 'undefined') {
57696         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57697         this.cm = this.colModel;
57698         this.cm.xmodule = this.xmodule || false;
57699     }
57700     if (this.dataSource) {
57701         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57702         this.ds = this.dataSource;
57703         this.ds.xmodule = this.xmodule || false;
57704          
57705     }
57706     
57707     
57708     
57709     if(this.width){
57710         this.container.setWidth(this.width);
57711     }
57712
57713     if(this.height){
57714         this.container.setHeight(this.height);
57715     }
57716     /** @private */
57717         this.addEvents({
57718         // raw events
57719         /**
57720          * @event click
57721          * The raw click event for the entire grid.
57722          * @param {Roo.EventObject} e
57723          */
57724         "click" : true,
57725         /**
57726          * @event dblclick
57727          * The raw dblclick event for the entire grid.
57728          * @param {Roo.EventObject} e
57729          */
57730         "dblclick" : true,
57731         /**
57732          * @event contextmenu
57733          * The raw contextmenu event for the entire grid.
57734          * @param {Roo.EventObject} e
57735          */
57736         "contextmenu" : true,
57737         /**
57738          * @event mousedown
57739          * The raw mousedown event for the entire grid.
57740          * @param {Roo.EventObject} e
57741          */
57742         "mousedown" : true,
57743         /**
57744          * @event mouseup
57745          * The raw mouseup event for the entire grid.
57746          * @param {Roo.EventObject} e
57747          */
57748         "mouseup" : true,
57749         /**
57750          * @event mouseover
57751          * The raw mouseover event for the entire grid.
57752          * @param {Roo.EventObject} e
57753          */
57754         "mouseover" : true,
57755         /**
57756          * @event mouseout
57757          * The raw mouseout event for the entire grid.
57758          * @param {Roo.EventObject} e
57759          */
57760         "mouseout" : true,
57761         /**
57762          * @event keypress
57763          * The raw keypress event for the entire grid.
57764          * @param {Roo.EventObject} e
57765          */
57766         "keypress" : true,
57767         /**
57768          * @event keydown
57769          * The raw keydown event for the entire grid.
57770          * @param {Roo.EventObject} e
57771          */
57772         "keydown" : true,
57773
57774         // custom events
57775
57776         /**
57777          * @event cellclick
57778          * Fires when a cell is clicked
57779          * @param {Grid} this
57780          * @param {Number} rowIndex
57781          * @param {Number} columnIndex
57782          * @param {Roo.EventObject} e
57783          */
57784         "cellclick" : true,
57785         /**
57786          * @event celldblclick
57787          * Fires when a cell is double clicked
57788          * @param {Grid} this
57789          * @param {Number} rowIndex
57790          * @param {Number} columnIndex
57791          * @param {Roo.EventObject} e
57792          */
57793         "celldblclick" : true,
57794         /**
57795          * @event rowclick
57796          * Fires when a row is clicked
57797          * @param {Grid} this
57798          * @param {Number} rowIndex
57799          * @param {Roo.EventObject} e
57800          */
57801         "rowclick" : true,
57802         /**
57803          * @event rowdblclick
57804          * Fires when a row is double clicked
57805          * @param {Grid} this
57806          * @param {Number} rowIndex
57807          * @param {Roo.EventObject} e
57808          */
57809         "rowdblclick" : true,
57810         /**
57811          * @event headerclick
57812          * Fires when a header is clicked
57813          * @param {Grid} this
57814          * @param {Number} columnIndex
57815          * @param {Roo.EventObject} e
57816          */
57817         "headerclick" : true,
57818         /**
57819          * @event headerdblclick
57820          * Fires when a header cell is double clicked
57821          * @param {Grid} this
57822          * @param {Number} columnIndex
57823          * @param {Roo.EventObject} e
57824          */
57825         "headerdblclick" : true,
57826         /**
57827          * @event rowcontextmenu
57828          * Fires when a row is right clicked
57829          * @param {Grid} this
57830          * @param {Number} rowIndex
57831          * @param {Roo.EventObject} e
57832          */
57833         "rowcontextmenu" : true,
57834         /**
57835          * @event cellcontextmenu
57836          * Fires when a cell is right clicked
57837          * @param {Grid} this
57838          * @param {Number} rowIndex
57839          * @param {Number} cellIndex
57840          * @param {Roo.EventObject} e
57841          */
57842          "cellcontextmenu" : true,
57843         /**
57844          * @event headercontextmenu
57845          * Fires when a header is right clicked
57846          * @param {Grid} this
57847          * @param {Number} columnIndex
57848          * @param {Roo.EventObject} e
57849          */
57850         "headercontextmenu" : true,
57851         /**
57852          * @event bodyscroll
57853          * Fires when the body element is scrolled
57854          * @param {Number} scrollLeft
57855          * @param {Number} scrollTop
57856          */
57857         "bodyscroll" : true,
57858         /**
57859          * @event columnresize
57860          * Fires when the user resizes a column
57861          * @param {Number} columnIndex
57862          * @param {Number} newSize
57863          */
57864         "columnresize" : true,
57865         /**
57866          * @event columnmove
57867          * Fires when the user moves a column
57868          * @param {Number} oldIndex
57869          * @param {Number} newIndex
57870          */
57871         "columnmove" : true,
57872         /**
57873          * @event startdrag
57874          * Fires when row(s) start being dragged
57875          * @param {Grid} this
57876          * @param {Roo.GridDD} dd The drag drop object
57877          * @param {event} e The raw browser event
57878          */
57879         "startdrag" : true,
57880         /**
57881          * @event enddrag
57882          * Fires when a drag operation is complete
57883          * @param {Grid} this
57884          * @param {Roo.GridDD} dd The drag drop object
57885          * @param {event} e The raw browser event
57886          */
57887         "enddrag" : true,
57888         /**
57889          * @event dragdrop
57890          * Fires when dragged row(s) are dropped on a valid DD target
57891          * @param {Grid} this
57892          * @param {Roo.GridDD} dd The drag drop object
57893          * @param {String} targetId The target drag drop object
57894          * @param {event} e The raw browser event
57895          */
57896         "dragdrop" : true,
57897         /**
57898          * @event dragover
57899          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57900          * @param {Grid} this
57901          * @param {Roo.GridDD} dd The drag drop object
57902          * @param {String} targetId The target drag drop object
57903          * @param {event} e The raw browser event
57904          */
57905         "dragover" : true,
57906         /**
57907          * @event dragenter
57908          *  Fires when the dragged row(s) first cross another DD target while being dragged
57909          * @param {Grid} this
57910          * @param {Roo.GridDD} dd The drag drop object
57911          * @param {String} targetId The target drag drop object
57912          * @param {event} e The raw browser event
57913          */
57914         "dragenter" : true,
57915         /**
57916          * @event dragout
57917          * Fires when the dragged row(s) leave another DD target while being dragged
57918          * @param {Grid} this
57919          * @param {Roo.GridDD} dd The drag drop object
57920          * @param {String} targetId The target drag drop object
57921          * @param {event} e The raw browser event
57922          */
57923         "dragout" : true,
57924         /**
57925          * @event rowclass
57926          * Fires when a row is rendered, so you can change add a style to it.
57927          * @param {GridView} gridview   The grid view
57928          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57929          */
57930         'rowclass' : true,
57931
57932         /**
57933          * @event render
57934          * Fires when the grid is rendered
57935          * @param {Grid} grid
57936          */
57937         'render' : true
57938     });
57939
57940     Roo.grid.Grid.superclass.constructor.call(this);
57941 };
57942 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57943     
57944     /**
57945          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57946          */
57947         /**
57948          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57949          */
57950         /**
57951          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57952          */
57953         /**
57954          * @cfg {Roo.grid.Store} ds The data store for the grid
57955          */
57956         /**
57957          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57958          */
57959         /**
57960      * @cfg {String} ddGroup - drag drop group.
57961      */
57962       /**
57963      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57964      */
57965
57966     /**
57967      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57968      */
57969     minColumnWidth : 25,
57970
57971     /**
57972      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57973      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57974      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57975      */
57976     autoSizeColumns : false,
57977
57978     /**
57979      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57980      */
57981     autoSizeHeaders : true,
57982
57983     /**
57984      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
57985      */
57986     monitorWindowResize : true,
57987
57988     /**
57989      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
57990      * rows measured to get a columns size. Default is 0 (all rows).
57991      */
57992     maxRowsToMeasure : 0,
57993
57994     /**
57995      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
57996      */
57997     trackMouseOver : true,
57998
57999     /**
58000     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58001     */
58002       /**
58003     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58004     */
58005     
58006     /**
58007     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58008     */
58009     enableDragDrop : false,
58010     
58011     /**
58012     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58013     */
58014     enableColumnMove : true,
58015     
58016     /**
58017     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58018     */
58019     enableColumnHide : true,
58020     
58021     /**
58022     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58023     */
58024     enableRowHeightSync : false,
58025     
58026     /**
58027     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58028     */
58029     stripeRows : true,
58030     
58031     /**
58032     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58033     */
58034     autoHeight : false,
58035
58036     /**
58037      * @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.
58038      */
58039     autoExpandColumn : false,
58040
58041     /**
58042     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58043     * Default is 50.
58044     */
58045     autoExpandMin : 50,
58046
58047     /**
58048     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58049     */
58050     autoExpandMax : 1000,
58051
58052     /**
58053     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58054     */
58055     view : null,
58056
58057     /**
58058     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58059     */
58060     loadMask : false,
58061     /**
58062     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58063     */
58064     dropTarget: false,
58065     
58066    
58067     
58068     // private
58069     rendered : false,
58070
58071     /**
58072     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58073     * of a fixed width. Default is false.
58074     */
58075     /**
58076     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58077     */
58078     
58079     
58080     /**
58081     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58082     * %0 is replaced with the number of selected rows.
58083     */
58084     ddText : "{0} selected row{1}",
58085     
58086     
58087     /**
58088      * Called once after all setup has been completed and the grid is ready to be rendered.
58089      * @return {Roo.grid.Grid} this
58090      */
58091     render : function()
58092     {
58093         var c = this.container;
58094         // try to detect autoHeight/width mode
58095         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58096             this.autoHeight = true;
58097         }
58098         var view = this.getView();
58099         view.init(this);
58100
58101         c.on("click", this.onClick, this);
58102         c.on("dblclick", this.onDblClick, this);
58103         c.on("contextmenu", this.onContextMenu, this);
58104         c.on("keydown", this.onKeyDown, this);
58105         if (Roo.isTouch) {
58106             c.on("touchstart", this.onTouchStart, this);
58107         }
58108
58109         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58110
58111         this.getSelectionModel().init(this);
58112
58113         view.render();
58114
58115         if(this.loadMask){
58116             this.loadMask = new Roo.LoadMask(this.container,
58117                     Roo.apply({store:this.dataSource}, this.loadMask));
58118         }
58119         
58120         
58121         if (this.toolbar && this.toolbar.xtype) {
58122             this.toolbar.container = this.getView().getHeaderPanel(true);
58123             this.toolbar = new Roo.Toolbar(this.toolbar);
58124         }
58125         if (this.footer && this.footer.xtype) {
58126             this.footer.dataSource = this.getDataSource();
58127             this.footer.container = this.getView().getFooterPanel(true);
58128             this.footer = Roo.factory(this.footer, Roo);
58129         }
58130         if (this.dropTarget && this.dropTarget.xtype) {
58131             delete this.dropTarget.xtype;
58132             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58133         }
58134         
58135         
58136         this.rendered = true;
58137         this.fireEvent('render', this);
58138         return this;
58139     },
58140
58141     /**
58142      * Reconfigures the grid to use a different Store and Column Model.
58143      * The View will be bound to the new objects and refreshed.
58144      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58145      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58146      */
58147     reconfigure : function(dataSource, colModel){
58148         if(this.loadMask){
58149             this.loadMask.destroy();
58150             this.loadMask = new Roo.LoadMask(this.container,
58151                     Roo.apply({store:dataSource}, this.loadMask));
58152         }
58153         this.view.bind(dataSource, colModel);
58154         this.dataSource = dataSource;
58155         this.colModel = colModel;
58156         this.view.refresh(true);
58157     },
58158     /**
58159      * addColumns
58160      * Add's a column, default at the end..
58161      
58162      * @param {int} position to add (default end)
58163      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58164      */
58165     addColumns : function(pos, ar)
58166     {
58167         
58168         for (var i =0;i< ar.length;i++) {
58169             var cfg = ar[i];
58170             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58171             this.cm.lookup[cfg.id] = cfg;
58172         }
58173         
58174         
58175         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58176             pos = this.cm.config.length; //this.cm.config.push(cfg);
58177         } 
58178         pos = Math.max(0,pos);
58179         ar.unshift(0);
58180         ar.unshift(pos);
58181         this.cm.config.splice.apply(this.cm.config, ar);
58182         
58183         
58184         
58185         this.view.generateRules(this.cm);
58186         this.view.refresh(true);
58187         
58188     },
58189     
58190     
58191     
58192     
58193     // private
58194     onKeyDown : function(e){
58195         this.fireEvent("keydown", e);
58196     },
58197
58198     /**
58199      * Destroy this grid.
58200      * @param {Boolean} removeEl True to remove the element
58201      */
58202     destroy : function(removeEl, keepListeners){
58203         if(this.loadMask){
58204             this.loadMask.destroy();
58205         }
58206         var c = this.container;
58207         c.removeAllListeners();
58208         this.view.destroy();
58209         this.colModel.purgeListeners();
58210         if(!keepListeners){
58211             this.purgeListeners();
58212         }
58213         c.update("");
58214         if(removeEl === true){
58215             c.remove();
58216         }
58217     },
58218
58219     // private
58220     processEvent : function(name, e){
58221         // does this fire select???
58222         //Roo.log('grid:processEvent '  + name);
58223         
58224         if (name != 'touchstart' ) {
58225             this.fireEvent(name, e);    
58226         }
58227         
58228         var t = e.getTarget();
58229         var v = this.view;
58230         var header = v.findHeaderIndex(t);
58231         if(header !== false){
58232             var ename = name == 'touchstart' ? 'click' : name;
58233              
58234             this.fireEvent("header" + ename, this, header, e);
58235         }else{
58236             var row = v.findRowIndex(t);
58237             var cell = v.findCellIndex(t);
58238             if (name == 'touchstart') {
58239                 // first touch is always a click.
58240                 // hopefull this happens after selection is updated.?
58241                 name = false;
58242                 
58243                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58244                     var cs = this.selModel.getSelectedCell();
58245                     if (row == cs[0] && cell == cs[1]){
58246                         name = 'dblclick';
58247                     }
58248                 }
58249                 if (typeof(this.selModel.getSelections) != 'undefined') {
58250                     var cs = this.selModel.getSelections();
58251                     var ds = this.dataSource;
58252                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58253                         name = 'dblclick';
58254                     }
58255                 }
58256                 if (!name) {
58257                     return;
58258                 }
58259             }
58260             
58261             
58262             if(row !== false){
58263                 this.fireEvent("row" + name, this, row, e);
58264                 if(cell !== false){
58265                     this.fireEvent("cell" + name, this, row, cell, e);
58266                 }
58267             }
58268         }
58269     },
58270
58271     // private
58272     onClick : function(e){
58273         this.processEvent("click", e);
58274     },
58275    // private
58276     onTouchStart : function(e){
58277         this.processEvent("touchstart", e);
58278     },
58279
58280     // private
58281     onContextMenu : function(e, t){
58282         this.processEvent("contextmenu", e);
58283     },
58284
58285     // private
58286     onDblClick : function(e){
58287         this.processEvent("dblclick", e);
58288     },
58289
58290     // private
58291     walkCells : function(row, col, step, fn, scope){
58292         var cm = this.colModel, clen = cm.getColumnCount();
58293         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58294         if(step < 0){
58295             if(col < 0){
58296                 row--;
58297                 first = false;
58298             }
58299             while(row >= 0){
58300                 if(!first){
58301                     col = clen-1;
58302                 }
58303                 first = false;
58304                 while(col >= 0){
58305                     if(fn.call(scope || this, row, col, cm) === true){
58306                         return [row, col];
58307                     }
58308                     col--;
58309                 }
58310                 row--;
58311             }
58312         } else {
58313             if(col >= clen){
58314                 row++;
58315                 first = false;
58316             }
58317             while(row < rlen){
58318                 if(!first){
58319                     col = 0;
58320                 }
58321                 first = false;
58322                 while(col < clen){
58323                     if(fn.call(scope || this, row, col, cm) === true){
58324                         return [row, col];
58325                     }
58326                     col++;
58327                 }
58328                 row++;
58329             }
58330         }
58331         return null;
58332     },
58333
58334     // private
58335     getSelections : function(){
58336         return this.selModel.getSelections();
58337     },
58338
58339     /**
58340      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58341      * but if manual update is required this method will initiate it.
58342      */
58343     autoSize : function(){
58344         if(this.rendered){
58345             this.view.layout();
58346             if(this.view.adjustForScroll){
58347                 this.view.adjustForScroll();
58348             }
58349         }
58350     },
58351
58352     /**
58353      * Returns the grid's underlying element.
58354      * @return {Element} The element
58355      */
58356     getGridEl : function(){
58357         return this.container;
58358     },
58359
58360     // private for compatibility, overridden by editor grid
58361     stopEditing : function(){},
58362
58363     /**
58364      * Returns the grid's SelectionModel.
58365      * @return {SelectionModel}
58366      */
58367     getSelectionModel : function(){
58368         if(!this.selModel){
58369             this.selModel = new Roo.grid.RowSelectionModel();
58370         }
58371         return this.selModel;
58372     },
58373
58374     /**
58375      * Returns the grid's DataSource.
58376      * @return {DataSource}
58377      */
58378     getDataSource : function(){
58379         return this.dataSource;
58380     },
58381
58382     /**
58383      * Returns the grid's ColumnModel.
58384      * @return {ColumnModel}
58385      */
58386     getColumnModel : function(){
58387         return this.colModel;
58388     },
58389
58390     /**
58391      * Returns the grid's GridView object.
58392      * @return {GridView}
58393      */
58394     getView : function(){
58395         if(!this.view){
58396             this.view = new Roo.grid.GridView(this.viewConfig);
58397             this.relayEvents(this.view, [
58398                 "beforerowremoved", "beforerowsinserted",
58399                 "beforerefresh", "rowremoved",
58400                 "rowsinserted", "rowupdated" ,"refresh"
58401             ]);
58402         }
58403         return this.view;
58404     },
58405     /**
58406      * Called to get grid's drag proxy text, by default returns this.ddText.
58407      * Override this to put something different in the dragged text.
58408      * @return {String}
58409      */
58410     getDragDropText : function(){
58411         var count = this.selModel.getCount();
58412         return String.format(this.ddText, count, count == 1 ? '' : 's');
58413     }
58414 });
58415 /*
58416  * Based on:
58417  * Ext JS Library 1.1.1
58418  * Copyright(c) 2006-2007, Ext JS, LLC.
58419  *
58420  * Originally Released Under LGPL - original licence link has changed is not relivant.
58421  *
58422  * Fork - LGPL
58423  * <script type="text/javascript">
58424  */
58425  /**
58426  * @class Roo.grid.AbstractGridView
58427  * @extends Roo.util.Observable
58428  * @abstract
58429  * Abstract base class for grid Views
58430  * @constructor
58431  */
58432 Roo.grid.AbstractGridView = function(){
58433         this.grid = null;
58434         
58435         this.events = {
58436             "beforerowremoved" : true,
58437             "beforerowsinserted" : true,
58438             "beforerefresh" : true,
58439             "rowremoved" : true,
58440             "rowsinserted" : true,
58441             "rowupdated" : true,
58442             "refresh" : true
58443         };
58444     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58445 };
58446
58447 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58448     rowClass : "x-grid-row",
58449     cellClass : "x-grid-cell",
58450     tdClass : "x-grid-td",
58451     hdClass : "x-grid-hd",
58452     splitClass : "x-grid-hd-split",
58453     
58454     init: function(grid){
58455         this.grid = grid;
58456                 var cid = this.grid.getGridEl().id;
58457         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58458         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58459         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58460         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58461         },
58462         
58463     getColumnRenderers : function(){
58464         var renderers = [];
58465         var cm = this.grid.colModel;
58466         var colCount = cm.getColumnCount();
58467         for(var i = 0; i < colCount; i++){
58468             renderers[i] = cm.getRenderer(i);
58469         }
58470         return renderers;
58471     },
58472     
58473     getColumnIds : function(){
58474         var ids = [];
58475         var cm = this.grid.colModel;
58476         var colCount = cm.getColumnCount();
58477         for(var i = 0; i < colCount; i++){
58478             ids[i] = cm.getColumnId(i);
58479         }
58480         return ids;
58481     },
58482     
58483     getDataIndexes : function(){
58484         if(!this.indexMap){
58485             this.indexMap = this.buildIndexMap();
58486         }
58487         return this.indexMap.colToData;
58488     },
58489     
58490     getColumnIndexByDataIndex : function(dataIndex){
58491         if(!this.indexMap){
58492             this.indexMap = this.buildIndexMap();
58493         }
58494         return this.indexMap.dataToCol[dataIndex];
58495     },
58496     
58497     /**
58498      * Set a css style for a column dynamically. 
58499      * @param {Number} colIndex The index of the column
58500      * @param {String} name The css property name
58501      * @param {String} value The css value
58502      */
58503     setCSSStyle : function(colIndex, name, value){
58504         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58505         Roo.util.CSS.updateRule(selector, name, value);
58506     },
58507     
58508     generateRules : function(cm){
58509         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58510         Roo.util.CSS.removeStyleSheet(rulesId);
58511         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58512             var cid = cm.getColumnId(i);
58513             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58514                          this.tdSelector, cid, " {\n}\n",
58515                          this.hdSelector, cid, " {\n}\n",
58516                          this.splitSelector, cid, " {\n}\n");
58517         }
58518         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58519     }
58520 });/*
58521  * Based on:
58522  * Ext JS Library 1.1.1
58523  * Copyright(c) 2006-2007, Ext JS, LLC.
58524  *
58525  * Originally Released Under LGPL - original licence link has changed is not relivant.
58526  *
58527  * Fork - LGPL
58528  * <script type="text/javascript">
58529  */
58530
58531 // private
58532 // This is a support class used internally by the Grid components
58533 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58534     this.grid = grid;
58535     this.view = grid.getView();
58536     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58537     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58538     if(hd2){
58539         this.setHandleElId(Roo.id(hd));
58540         this.setOuterHandleElId(Roo.id(hd2));
58541     }
58542     this.scroll = false;
58543 };
58544 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58545     maxDragWidth: 120,
58546     getDragData : function(e){
58547         var t = Roo.lib.Event.getTarget(e);
58548         var h = this.view.findHeaderCell(t);
58549         if(h){
58550             return {ddel: h.firstChild, header:h};
58551         }
58552         return false;
58553     },
58554
58555     onInitDrag : function(e){
58556         this.view.headersDisabled = true;
58557         var clone = this.dragData.ddel.cloneNode(true);
58558         clone.id = Roo.id();
58559         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58560         this.proxy.update(clone);
58561         return true;
58562     },
58563
58564     afterValidDrop : function(){
58565         var v = this.view;
58566         setTimeout(function(){
58567             v.headersDisabled = false;
58568         }, 50);
58569     },
58570
58571     afterInvalidDrop : function(){
58572         var v = this.view;
58573         setTimeout(function(){
58574             v.headersDisabled = false;
58575         }, 50);
58576     }
58577 });
58578 /*
58579  * Based on:
58580  * Ext JS Library 1.1.1
58581  * Copyright(c) 2006-2007, Ext JS, LLC.
58582  *
58583  * Originally Released Under LGPL - original licence link has changed is not relivant.
58584  *
58585  * Fork - LGPL
58586  * <script type="text/javascript">
58587  */
58588 // private
58589 // This is a support class used internally by the Grid components
58590 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58591     this.grid = grid;
58592     this.view = grid.getView();
58593     // split the proxies so they don't interfere with mouse events
58594     this.proxyTop = Roo.DomHelper.append(document.body, {
58595         cls:"col-move-top", html:"&#160;"
58596     }, true);
58597     this.proxyBottom = Roo.DomHelper.append(document.body, {
58598         cls:"col-move-bottom", html:"&#160;"
58599     }, true);
58600     this.proxyTop.hide = this.proxyBottom.hide = function(){
58601         this.setLeftTop(-100,-100);
58602         this.setStyle("visibility", "hidden");
58603     };
58604     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58605     // temporarily disabled
58606     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58607     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58608 };
58609 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58610     proxyOffsets : [-4, -9],
58611     fly: Roo.Element.fly,
58612
58613     getTargetFromEvent : function(e){
58614         var t = Roo.lib.Event.getTarget(e);
58615         var cindex = this.view.findCellIndex(t);
58616         if(cindex !== false){
58617             return this.view.getHeaderCell(cindex);
58618         }
58619         return null;
58620     },
58621
58622     nextVisible : function(h){
58623         var v = this.view, cm = this.grid.colModel;
58624         h = h.nextSibling;
58625         while(h){
58626             if(!cm.isHidden(v.getCellIndex(h))){
58627                 return h;
58628             }
58629             h = h.nextSibling;
58630         }
58631         return null;
58632     },
58633
58634     prevVisible : function(h){
58635         var v = this.view, cm = this.grid.colModel;
58636         h = h.prevSibling;
58637         while(h){
58638             if(!cm.isHidden(v.getCellIndex(h))){
58639                 return h;
58640             }
58641             h = h.prevSibling;
58642         }
58643         return null;
58644     },
58645
58646     positionIndicator : function(h, n, e){
58647         var x = Roo.lib.Event.getPageX(e);
58648         var r = Roo.lib.Dom.getRegion(n.firstChild);
58649         var px, pt, py = r.top + this.proxyOffsets[1];
58650         if((r.right - x) <= (r.right-r.left)/2){
58651             px = r.right+this.view.borderWidth;
58652             pt = "after";
58653         }else{
58654             px = r.left;
58655             pt = "before";
58656         }
58657         var oldIndex = this.view.getCellIndex(h);
58658         var newIndex = this.view.getCellIndex(n);
58659
58660         if(this.grid.colModel.isFixed(newIndex)){
58661             return false;
58662         }
58663
58664         var locked = this.grid.colModel.isLocked(newIndex);
58665
58666         if(pt == "after"){
58667             newIndex++;
58668         }
58669         if(oldIndex < newIndex){
58670             newIndex--;
58671         }
58672         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58673             return false;
58674         }
58675         px +=  this.proxyOffsets[0];
58676         this.proxyTop.setLeftTop(px, py);
58677         this.proxyTop.show();
58678         if(!this.bottomOffset){
58679             this.bottomOffset = this.view.mainHd.getHeight();
58680         }
58681         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58682         this.proxyBottom.show();
58683         return pt;
58684     },
58685
58686     onNodeEnter : function(n, dd, e, data){
58687         if(data.header != n){
58688             this.positionIndicator(data.header, n, e);
58689         }
58690     },
58691
58692     onNodeOver : function(n, dd, e, data){
58693         var result = false;
58694         if(data.header != n){
58695             result = this.positionIndicator(data.header, n, e);
58696         }
58697         if(!result){
58698             this.proxyTop.hide();
58699             this.proxyBottom.hide();
58700         }
58701         return result ? this.dropAllowed : this.dropNotAllowed;
58702     },
58703
58704     onNodeOut : function(n, dd, e, data){
58705         this.proxyTop.hide();
58706         this.proxyBottom.hide();
58707     },
58708
58709     onNodeDrop : function(n, dd, e, data){
58710         var h = data.header;
58711         if(h != n){
58712             var cm = this.grid.colModel;
58713             var x = Roo.lib.Event.getPageX(e);
58714             var r = Roo.lib.Dom.getRegion(n.firstChild);
58715             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58716             var oldIndex = this.view.getCellIndex(h);
58717             var newIndex = this.view.getCellIndex(n);
58718             var locked = cm.isLocked(newIndex);
58719             if(pt == "after"){
58720                 newIndex++;
58721             }
58722             if(oldIndex < newIndex){
58723                 newIndex--;
58724             }
58725             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58726                 return false;
58727             }
58728             cm.setLocked(oldIndex, locked, true);
58729             cm.moveColumn(oldIndex, newIndex);
58730             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58731             return true;
58732         }
58733         return false;
58734     }
58735 });
58736 /*
58737  * Based on:
58738  * Ext JS Library 1.1.1
58739  * Copyright(c) 2006-2007, Ext JS, LLC.
58740  *
58741  * Originally Released Under LGPL - original licence link has changed is not relivant.
58742  *
58743  * Fork - LGPL
58744  * <script type="text/javascript">
58745  */
58746   
58747 /**
58748  * @class Roo.grid.GridView
58749  * @extends Roo.util.Observable
58750  *
58751  * @constructor
58752  * @param {Object} config
58753  */
58754 Roo.grid.GridView = function(config){
58755     Roo.grid.GridView.superclass.constructor.call(this);
58756     this.el = null;
58757
58758     Roo.apply(this, config);
58759 };
58760
58761 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58762
58763     unselectable :  'unselectable="on"',
58764     unselectableCls :  'x-unselectable',
58765     
58766     
58767     rowClass : "x-grid-row",
58768
58769     cellClass : "x-grid-col",
58770
58771     tdClass : "x-grid-td",
58772
58773     hdClass : "x-grid-hd",
58774
58775     splitClass : "x-grid-split",
58776
58777     sortClasses : ["sort-asc", "sort-desc"],
58778
58779     enableMoveAnim : false,
58780
58781     hlColor: "C3DAF9",
58782
58783     dh : Roo.DomHelper,
58784
58785     fly : Roo.Element.fly,
58786
58787     css : Roo.util.CSS,
58788
58789     borderWidth: 1,
58790
58791     splitOffset: 3,
58792
58793     scrollIncrement : 22,
58794
58795     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58796
58797     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58798
58799     bind : function(ds, cm){
58800         if(this.ds){
58801             this.ds.un("load", this.onLoad, this);
58802             this.ds.un("datachanged", this.onDataChange, this);
58803             this.ds.un("add", this.onAdd, this);
58804             this.ds.un("remove", this.onRemove, this);
58805             this.ds.un("update", this.onUpdate, this);
58806             this.ds.un("clear", this.onClear, this);
58807         }
58808         if(ds){
58809             ds.on("load", this.onLoad, this);
58810             ds.on("datachanged", this.onDataChange, this);
58811             ds.on("add", this.onAdd, this);
58812             ds.on("remove", this.onRemove, this);
58813             ds.on("update", this.onUpdate, this);
58814             ds.on("clear", this.onClear, this);
58815         }
58816         this.ds = ds;
58817
58818         if(this.cm){
58819             this.cm.un("widthchange", this.onColWidthChange, this);
58820             this.cm.un("headerchange", this.onHeaderChange, this);
58821             this.cm.un("hiddenchange", this.onHiddenChange, this);
58822             this.cm.un("columnmoved", this.onColumnMove, this);
58823             this.cm.un("columnlockchange", this.onColumnLock, this);
58824         }
58825         if(cm){
58826             this.generateRules(cm);
58827             cm.on("widthchange", this.onColWidthChange, this);
58828             cm.on("headerchange", this.onHeaderChange, this);
58829             cm.on("hiddenchange", this.onHiddenChange, this);
58830             cm.on("columnmoved", this.onColumnMove, this);
58831             cm.on("columnlockchange", this.onColumnLock, this);
58832         }
58833         this.cm = cm;
58834     },
58835
58836     init: function(grid){
58837         Roo.grid.GridView.superclass.init.call(this, grid);
58838
58839         this.bind(grid.dataSource, grid.colModel);
58840
58841         grid.on("headerclick", this.handleHeaderClick, this);
58842
58843         if(grid.trackMouseOver){
58844             grid.on("mouseover", this.onRowOver, this);
58845             grid.on("mouseout", this.onRowOut, this);
58846         }
58847         grid.cancelTextSelection = function(){};
58848         this.gridId = grid.id;
58849
58850         var tpls = this.templates || {};
58851
58852         if(!tpls.master){
58853             tpls.master = new Roo.Template(
58854                '<div class="x-grid" hidefocus="true">',
58855                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58856                   '<div class="x-grid-topbar"></div>',
58857                   '<div class="x-grid-scroller"><div></div></div>',
58858                   '<div class="x-grid-locked">',
58859                       '<div class="x-grid-header">{lockedHeader}</div>',
58860                       '<div class="x-grid-body">{lockedBody}</div>',
58861                   "</div>",
58862                   '<div class="x-grid-viewport">',
58863                       '<div class="x-grid-header">{header}</div>',
58864                       '<div class="x-grid-body">{body}</div>',
58865                   "</div>",
58866                   '<div class="x-grid-bottombar"></div>',
58867                  
58868                   '<div class="x-grid-resize-proxy">&#160;</div>',
58869                "</div>"
58870             );
58871             tpls.master.disableformats = true;
58872         }
58873
58874         if(!tpls.header){
58875             tpls.header = new Roo.Template(
58876                '<table border="0" cellspacing="0" cellpadding="0">',
58877                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58878                "</table>{splits}"
58879             );
58880             tpls.header.disableformats = true;
58881         }
58882         tpls.header.compile();
58883
58884         if(!tpls.hcell){
58885             tpls.hcell = new Roo.Template(
58886                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58887                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58888                 "</div></td>"
58889              );
58890              tpls.hcell.disableFormats = true;
58891         }
58892         tpls.hcell.compile();
58893
58894         if(!tpls.hsplit){
58895             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58896                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58897             tpls.hsplit.disableFormats = true;
58898         }
58899         tpls.hsplit.compile();
58900
58901         if(!tpls.body){
58902             tpls.body = new Roo.Template(
58903                '<table border="0" cellspacing="0" cellpadding="0">',
58904                "<tbody>{rows}</tbody>",
58905                "</table>"
58906             );
58907             tpls.body.disableFormats = true;
58908         }
58909         tpls.body.compile();
58910
58911         if(!tpls.row){
58912             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58913             tpls.row.disableFormats = true;
58914         }
58915         tpls.row.compile();
58916
58917         if(!tpls.cell){
58918             tpls.cell = new Roo.Template(
58919                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58920                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58921                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58922                 "</td>"
58923             );
58924             tpls.cell.disableFormats = true;
58925         }
58926         tpls.cell.compile();
58927
58928         this.templates = tpls;
58929     },
58930
58931     // remap these for backwards compat
58932     onColWidthChange : function(){
58933         this.updateColumns.apply(this, arguments);
58934     },
58935     onHeaderChange : function(){
58936         this.updateHeaders.apply(this, arguments);
58937     }, 
58938     onHiddenChange : function(){
58939         this.handleHiddenChange.apply(this, arguments);
58940     },
58941     onColumnMove : function(){
58942         this.handleColumnMove.apply(this, arguments);
58943     },
58944     onColumnLock : function(){
58945         this.handleLockChange.apply(this, arguments);
58946     },
58947
58948     onDataChange : function(){
58949         this.refresh();
58950         this.updateHeaderSortState();
58951     },
58952
58953     onClear : function(){
58954         this.refresh();
58955     },
58956
58957     onUpdate : function(ds, record){
58958         this.refreshRow(record);
58959     },
58960
58961     refreshRow : function(record){
58962         var ds = this.ds, index;
58963         if(typeof record == 'number'){
58964             index = record;
58965             record = ds.getAt(index);
58966         }else{
58967             index = ds.indexOf(record);
58968         }
58969         this.insertRows(ds, index, index, true);
58970         this.onRemove(ds, record, index+1, true);
58971         this.syncRowHeights(index, index);
58972         this.layout();
58973         this.fireEvent("rowupdated", this, index, record);
58974     },
58975
58976     onAdd : function(ds, records, index){
58977         this.insertRows(ds, index, index + (records.length-1));
58978     },
58979
58980     onRemove : function(ds, record, index, isUpdate){
58981         if(isUpdate !== true){
58982             this.fireEvent("beforerowremoved", this, index, record);
58983         }
58984         var bt = this.getBodyTable(), lt = this.getLockedTable();
58985         if(bt.rows[index]){
58986             bt.firstChild.removeChild(bt.rows[index]);
58987         }
58988         if(lt.rows[index]){
58989             lt.firstChild.removeChild(lt.rows[index]);
58990         }
58991         if(isUpdate !== true){
58992             this.stripeRows(index);
58993             this.syncRowHeights(index, index);
58994             this.layout();
58995             this.fireEvent("rowremoved", this, index, record);
58996         }
58997     },
58998
58999     onLoad : function(){
59000         this.scrollToTop();
59001     },
59002
59003     /**
59004      * Scrolls the grid to the top
59005      */
59006     scrollToTop : function(){
59007         if(this.scroller){
59008             this.scroller.dom.scrollTop = 0;
59009             this.syncScroll();
59010         }
59011     },
59012
59013     /**
59014      * Gets a panel in the header of the grid that can be used for toolbars etc.
59015      * After modifying the contents of this panel a call to grid.autoSize() may be
59016      * required to register any changes in size.
59017      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59018      * @return Roo.Element
59019      */
59020     getHeaderPanel : function(doShow){
59021         if(doShow){
59022             this.headerPanel.show();
59023         }
59024         return this.headerPanel;
59025     },
59026
59027     /**
59028      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59029      * After modifying the contents of this panel a call to grid.autoSize() may be
59030      * required to register any changes in size.
59031      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59032      * @return Roo.Element
59033      */
59034     getFooterPanel : function(doShow){
59035         if(doShow){
59036             this.footerPanel.show();
59037         }
59038         return this.footerPanel;
59039     },
59040
59041     initElements : function(){
59042         var E = Roo.Element;
59043         var el = this.grid.getGridEl().dom.firstChild;
59044         var cs = el.childNodes;
59045
59046         this.el = new E(el);
59047         
59048          this.focusEl = new E(el.firstChild);
59049         this.focusEl.swallowEvent("click", true);
59050         
59051         this.headerPanel = new E(cs[1]);
59052         this.headerPanel.enableDisplayMode("block");
59053
59054         this.scroller = new E(cs[2]);
59055         this.scrollSizer = new E(this.scroller.dom.firstChild);
59056
59057         this.lockedWrap = new E(cs[3]);
59058         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59059         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59060
59061         this.mainWrap = new E(cs[4]);
59062         this.mainHd = new E(this.mainWrap.dom.firstChild);
59063         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59064
59065         this.footerPanel = new E(cs[5]);
59066         this.footerPanel.enableDisplayMode("block");
59067
59068         this.resizeProxy = new E(cs[6]);
59069
59070         this.headerSelector = String.format(
59071            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59072            this.lockedHd.id, this.mainHd.id
59073         );
59074
59075         this.splitterSelector = String.format(
59076            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59077            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59078         );
59079     },
59080     idToCssName : function(s)
59081     {
59082         return s.replace(/[^a-z0-9]+/ig, '-');
59083     },
59084
59085     getHeaderCell : function(index){
59086         return Roo.DomQuery.select(this.headerSelector)[index];
59087     },
59088
59089     getHeaderCellMeasure : function(index){
59090         return this.getHeaderCell(index).firstChild;
59091     },
59092
59093     getHeaderCellText : function(index){
59094         return this.getHeaderCell(index).firstChild.firstChild;
59095     },
59096
59097     getLockedTable : function(){
59098         return this.lockedBody.dom.firstChild;
59099     },
59100
59101     getBodyTable : function(){
59102         return this.mainBody.dom.firstChild;
59103     },
59104
59105     getLockedRow : function(index){
59106         return this.getLockedTable().rows[index];
59107     },
59108
59109     getRow : function(index){
59110         return this.getBodyTable().rows[index];
59111     },
59112
59113     getRowComposite : function(index){
59114         if(!this.rowEl){
59115             this.rowEl = new Roo.CompositeElementLite();
59116         }
59117         var els = [], lrow, mrow;
59118         if(lrow = this.getLockedRow(index)){
59119             els.push(lrow);
59120         }
59121         if(mrow = this.getRow(index)){
59122             els.push(mrow);
59123         }
59124         this.rowEl.elements = els;
59125         return this.rowEl;
59126     },
59127     /**
59128      * Gets the 'td' of the cell
59129      * 
59130      * @param {Integer} rowIndex row to select
59131      * @param {Integer} colIndex column to select
59132      * 
59133      * @return {Object} 
59134      */
59135     getCell : function(rowIndex, colIndex){
59136         var locked = this.cm.getLockedCount();
59137         var source;
59138         if(colIndex < locked){
59139             source = this.lockedBody.dom.firstChild;
59140         }else{
59141             source = this.mainBody.dom.firstChild;
59142             colIndex -= locked;
59143         }
59144         return source.rows[rowIndex].childNodes[colIndex];
59145     },
59146
59147     getCellText : function(rowIndex, colIndex){
59148         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59149     },
59150
59151     getCellBox : function(cell){
59152         var b = this.fly(cell).getBox();
59153         if(Roo.isOpera){ // opera fails to report the Y
59154             b.y = cell.offsetTop + this.mainBody.getY();
59155         }
59156         return b;
59157     },
59158
59159     getCellIndex : function(cell){
59160         var id = String(cell.className).match(this.cellRE);
59161         if(id){
59162             return parseInt(id[1], 10);
59163         }
59164         return 0;
59165     },
59166
59167     findHeaderIndex : function(n){
59168         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59169         return r ? this.getCellIndex(r) : false;
59170     },
59171
59172     findHeaderCell : function(n){
59173         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59174         return r ? r : false;
59175     },
59176
59177     findRowIndex : function(n){
59178         if(!n){
59179             return false;
59180         }
59181         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59182         return r ? r.rowIndex : false;
59183     },
59184
59185     findCellIndex : function(node){
59186         var stop = this.el.dom;
59187         while(node && node != stop){
59188             if(this.findRE.test(node.className)){
59189                 return this.getCellIndex(node);
59190             }
59191             node = node.parentNode;
59192         }
59193         return false;
59194     },
59195
59196     getColumnId : function(index){
59197         return this.cm.getColumnId(index);
59198     },
59199
59200     getSplitters : function()
59201     {
59202         if(this.splitterSelector){
59203            return Roo.DomQuery.select(this.splitterSelector);
59204         }else{
59205             return null;
59206       }
59207     },
59208
59209     getSplitter : function(index){
59210         return this.getSplitters()[index];
59211     },
59212
59213     onRowOver : function(e, t){
59214         var row;
59215         if((row = this.findRowIndex(t)) !== false){
59216             this.getRowComposite(row).addClass("x-grid-row-over");
59217         }
59218     },
59219
59220     onRowOut : function(e, t){
59221         var row;
59222         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59223             this.getRowComposite(row).removeClass("x-grid-row-over");
59224         }
59225     },
59226
59227     renderHeaders : function(){
59228         var cm = this.cm;
59229         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59230         var cb = [], lb = [], sb = [], lsb = [], p = {};
59231         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59232             p.cellId = "x-grid-hd-0-" + i;
59233             p.splitId = "x-grid-csplit-0-" + i;
59234             p.id = cm.getColumnId(i);
59235             p.value = cm.getColumnHeader(i) || "";
59236             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59237             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59238             if(!cm.isLocked(i)){
59239                 cb[cb.length] = ct.apply(p);
59240                 sb[sb.length] = st.apply(p);
59241             }else{
59242                 lb[lb.length] = ct.apply(p);
59243                 lsb[lsb.length] = st.apply(p);
59244             }
59245         }
59246         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59247                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59248     },
59249
59250     updateHeaders : function(){
59251         var html = this.renderHeaders();
59252         this.lockedHd.update(html[0]);
59253         this.mainHd.update(html[1]);
59254     },
59255
59256     /**
59257      * Focuses the specified row.
59258      * @param {Number} row The row index
59259      */
59260     focusRow : function(row)
59261     {
59262         //Roo.log('GridView.focusRow');
59263         var x = this.scroller.dom.scrollLeft;
59264         this.focusCell(row, 0, false);
59265         this.scroller.dom.scrollLeft = x;
59266     },
59267
59268     /**
59269      * Focuses the specified cell.
59270      * @param {Number} row The row index
59271      * @param {Number} col The column index
59272      * @param {Boolean} hscroll false to disable horizontal scrolling
59273      */
59274     focusCell : function(row, col, hscroll)
59275     {
59276         //Roo.log('GridView.focusCell');
59277         var el = this.ensureVisible(row, col, hscroll);
59278         this.focusEl.alignTo(el, "tl-tl");
59279         if(Roo.isGecko){
59280             this.focusEl.focus();
59281         }else{
59282             this.focusEl.focus.defer(1, this.focusEl);
59283         }
59284     },
59285
59286     /**
59287      * Scrolls the specified cell into view
59288      * @param {Number} row The row index
59289      * @param {Number} col The column index
59290      * @param {Boolean} hscroll false to disable horizontal scrolling
59291      */
59292     ensureVisible : function(row, col, hscroll)
59293     {
59294         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59295         //return null; //disable for testing.
59296         if(typeof row != "number"){
59297             row = row.rowIndex;
59298         }
59299         if(row < 0 && row >= this.ds.getCount()){
59300             return  null;
59301         }
59302         col = (col !== undefined ? col : 0);
59303         var cm = this.grid.colModel;
59304         while(cm.isHidden(col)){
59305             col++;
59306         }
59307
59308         var el = this.getCell(row, col);
59309         if(!el){
59310             return null;
59311         }
59312         var c = this.scroller.dom;
59313
59314         var ctop = parseInt(el.offsetTop, 10);
59315         var cleft = parseInt(el.offsetLeft, 10);
59316         var cbot = ctop + el.offsetHeight;
59317         var cright = cleft + el.offsetWidth;
59318         
59319         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59320         var stop = parseInt(c.scrollTop, 10);
59321         var sleft = parseInt(c.scrollLeft, 10);
59322         var sbot = stop + ch;
59323         var sright = sleft + c.clientWidth;
59324         /*
59325         Roo.log('GridView.ensureVisible:' +
59326                 ' ctop:' + ctop +
59327                 ' c.clientHeight:' + c.clientHeight +
59328                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59329                 ' stop:' + stop +
59330                 ' cbot:' + cbot +
59331                 ' sbot:' + sbot +
59332                 ' ch:' + ch  
59333                 );
59334         */
59335         if(ctop < stop){
59336             c.scrollTop = ctop;
59337             //Roo.log("set scrolltop to ctop DISABLE?");
59338         }else if(cbot > sbot){
59339             //Roo.log("set scrolltop to cbot-ch");
59340             c.scrollTop = cbot-ch;
59341         }
59342         
59343         if(hscroll !== false){
59344             if(cleft < sleft){
59345                 c.scrollLeft = cleft;
59346             }else if(cright > sright){
59347                 c.scrollLeft = cright-c.clientWidth;
59348             }
59349         }
59350          
59351         return el;
59352     },
59353
59354     updateColumns : function(){
59355         this.grid.stopEditing();
59356         var cm = this.grid.colModel, colIds = this.getColumnIds();
59357         //var totalWidth = cm.getTotalWidth();
59358         var pos = 0;
59359         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59360             //if(cm.isHidden(i)) continue;
59361             var w = cm.getColumnWidth(i);
59362             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59363             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59364         }
59365         this.updateSplitters();
59366     },
59367
59368     generateRules : function(cm){
59369         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59370         Roo.util.CSS.removeStyleSheet(rulesId);
59371         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59372             var cid = cm.getColumnId(i);
59373             var align = '';
59374             if(cm.config[i].align){
59375                 align = 'text-align:'+cm.config[i].align+';';
59376             }
59377             var hidden = '';
59378             if(cm.isHidden(i)){
59379                 hidden = 'display:none;';
59380             }
59381             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59382             ruleBuf.push(
59383                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59384                     this.hdSelector, cid, " {\n", align, width, "}\n",
59385                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59386                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59387         }
59388         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59389     },
59390
59391     updateSplitters : function(){
59392         var cm = this.cm, s = this.getSplitters();
59393         if(s){ // splitters not created yet
59394             var pos = 0, locked = true;
59395             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59396                 if(cm.isHidden(i)) {
59397                     continue;
59398                 }
59399                 var w = cm.getColumnWidth(i); // make sure it's a number
59400                 if(!cm.isLocked(i) && locked){
59401                     pos = 0;
59402                     locked = false;
59403                 }
59404                 pos += w;
59405                 s[i].style.left = (pos-this.splitOffset) + "px";
59406             }
59407         }
59408     },
59409
59410     handleHiddenChange : function(colModel, colIndex, hidden){
59411         if(hidden){
59412             this.hideColumn(colIndex);
59413         }else{
59414             this.unhideColumn(colIndex);
59415         }
59416     },
59417
59418     hideColumn : function(colIndex){
59419         var cid = this.getColumnId(colIndex);
59420         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59421         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59422         if(Roo.isSafari){
59423             this.updateHeaders();
59424         }
59425         this.updateSplitters();
59426         this.layout();
59427     },
59428
59429     unhideColumn : function(colIndex){
59430         var cid = this.getColumnId(colIndex);
59431         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59432         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59433
59434         if(Roo.isSafari){
59435             this.updateHeaders();
59436         }
59437         this.updateSplitters();
59438         this.layout();
59439     },
59440
59441     insertRows : function(dm, firstRow, lastRow, isUpdate){
59442         if(firstRow == 0 && lastRow == dm.getCount()-1){
59443             this.refresh();
59444         }else{
59445             if(!isUpdate){
59446                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59447             }
59448             var s = this.getScrollState();
59449             var markup = this.renderRows(firstRow, lastRow);
59450             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59451             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59452             this.restoreScroll(s);
59453             if(!isUpdate){
59454                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59455                 this.syncRowHeights(firstRow, lastRow);
59456                 this.stripeRows(firstRow);
59457                 this.layout();
59458             }
59459         }
59460     },
59461
59462     bufferRows : function(markup, target, index){
59463         var before = null, trows = target.rows, tbody = target.tBodies[0];
59464         if(index < trows.length){
59465             before = trows[index];
59466         }
59467         var b = document.createElement("div");
59468         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59469         var rows = b.firstChild.rows;
59470         for(var i = 0, len = rows.length; i < len; i++){
59471             if(before){
59472                 tbody.insertBefore(rows[0], before);
59473             }else{
59474                 tbody.appendChild(rows[0]);
59475             }
59476         }
59477         b.innerHTML = "";
59478         b = null;
59479     },
59480
59481     deleteRows : function(dm, firstRow, lastRow){
59482         if(dm.getRowCount()<1){
59483             this.fireEvent("beforerefresh", this);
59484             this.mainBody.update("");
59485             this.lockedBody.update("");
59486             this.fireEvent("refresh", this);
59487         }else{
59488             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59489             var bt = this.getBodyTable();
59490             var tbody = bt.firstChild;
59491             var rows = bt.rows;
59492             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59493                 tbody.removeChild(rows[firstRow]);
59494             }
59495             this.stripeRows(firstRow);
59496             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59497         }
59498     },
59499
59500     updateRows : function(dataSource, firstRow, lastRow){
59501         var s = this.getScrollState();
59502         this.refresh();
59503         this.restoreScroll(s);
59504     },
59505
59506     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59507         if(!noRefresh){
59508            this.refresh();
59509         }
59510         this.updateHeaderSortState();
59511     },
59512
59513     getScrollState : function(){
59514         
59515         var sb = this.scroller.dom;
59516         return {left: sb.scrollLeft, top: sb.scrollTop};
59517     },
59518
59519     stripeRows : function(startRow){
59520         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59521             return;
59522         }
59523         startRow = startRow || 0;
59524         var rows = this.getBodyTable().rows;
59525         var lrows = this.getLockedTable().rows;
59526         var cls = ' x-grid-row-alt ';
59527         for(var i = startRow, len = rows.length; i < len; i++){
59528             var row = rows[i], lrow = lrows[i];
59529             var isAlt = ((i+1) % 2 == 0);
59530             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59531             if(isAlt == hasAlt){
59532                 continue;
59533             }
59534             if(isAlt){
59535                 row.className += " x-grid-row-alt";
59536             }else{
59537                 row.className = row.className.replace("x-grid-row-alt", "");
59538             }
59539             if(lrow){
59540                 lrow.className = row.className;
59541             }
59542         }
59543     },
59544
59545     restoreScroll : function(state){
59546         //Roo.log('GridView.restoreScroll');
59547         var sb = this.scroller.dom;
59548         sb.scrollLeft = state.left;
59549         sb.scrollTop = state.top;
59550         this.syncScroll();
59551     },
59552
59553     syncScroll : function(){
59554         //Roo.log('GridView.syncScroll');
59555         var sb = this.scroller.dom;
59556         var sh = this.mainHd.dom;
59557         var bs = this.mainBody.dom;
59558         var lv = this.lockedBody.dom;
59559         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59560         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59561     },
59562
59563     handleScroll : function(e){
59564         this.syncScroll();
59565         var sb = this.scroller.dom;
59566         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59567         e.stopEvent();
59568     },
59569
59570     handleWheel : function(e){
59571         var d = e.getWheelDelta();
59572         this.scroller.dom.scrollTop -= d*22;
59573         // set this here to prevent jumpy scrolling on large tables
59574         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59575         e.stopEvent();
59576     },
59577
59578     renderRows : function(startRow, endRow){
59579         // pull in all the crap needed to render rows
59580         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59581         var colCount = cm.getColumnCount();
59582
59583         if(ds.getCount() < 1){
59584             return ["", ""];
59585         }
59586
59587         // build a map for all the columns
59588         var cs = [];
59589         for(var i = 0; i < colCount; i++){
59590             var name = cm.getDataIndex(i);
59591             cs[i] = {
59592                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59593                 renderer : cm.getRenderer(i),
59594                 id : cm.getColumnId(i),
59595                 locked : cm.isLocked(i),
59596                 has_editor : cm.isCellEditable(i)
59597             };
59598         }
59599
59600         startRow = startRow || 0;
59601         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59602
59603         // records to render
59604         var rs = ds.getRange(startRow, endRow);
59605
59606         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59607     },
59608
59609     // As much as I hate to duplicate code, this was branched because FireFox really hates
59610     // [].join("") on strings. The performance difference was substantial enough to
59611     // branch this function
59612     doRender : Roo.isGecko ?
59613             function(cs, rs, ds, startRow, colCount, stripe){
59614                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59615                 // buffers
59616                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59617                 
59618                 var hasListener = this.grid.hasListener('rowclass');
59619                 var rowcfg = {};
59620                 for(var j = 0, len = rs.length; j < len; j++){
59621                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59622                     for(var i = 0; i < colCount; i++){
59623                         c = cs[i];
59624                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59625                         p.id = c.id;
59626                         p.css = p.attr = "";
59627                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59628                         if(p.value == undefined || p.value === "") {
59629                             p.value = "&#160;";
59630                         }
59631                         if(c.has_editor){
59632                             p.css += ' x-grid-editable-cell';
59633                         }
59634                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59635                             p.css +=  ' x-grid-dirty-cell';
59636                         }
59637                         var markup = ct.apply(p);
59638                         if(!c.locked){
59639                             cb+= markup;
59640                         }else{
59641                             lcb+= markup;
59642                         }
59643                     }
59644                     var alt = [];
59645                     if(stripe && ((rowIndex+1) % 2 == 0)){
59646                         alt.push("x-grid-row-alt")
59647                     }
59648                     if(r.dirty){
59649                         alt.push(  " x-grid-dirty-row");
59650                     }
59651                     rp.cells = lcb;
59652                     if(this.getRowClass){
59653                         alt.push(this.getRowClass(r, rowIndex));
59654                     }
59655                     if (hasListener) {
59656                         rowcfg = {
59657                              
59658                             record: r,
59659                             rowIndex : rowIndex,
59660                             rowClass : ''
59661                         };
59662                         this.grid.fireEvent('rowclass', this, rowcfg);
59663                         alt.push(rowcfg.rowClass);
59664                     }
59665                     rp.alt = alt.join(" ");
59666                     lbuf+= rt.apply(rp);
59667                     rp.cells = cb;
59668                     buf+=  rt.apply(rp);
59669                 }
59670                 return [lbuf, buf];
59671             } :
59672             function(cs, rs, ds, startRow, colCount, stripe){
59673                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59674                 // buffers
59675                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59676                 var hasListener = this.grid.hasListener('rowclass');
59677  
59678                 var rowcfg = {};
59679                 for(var j = 0, len = rs.length; j < len; j++){
59680                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59681                     for(var i = 0; i < colCount; i++){
59682                         c = cs[i];
59683                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59684                         p.id = c.id;
59685                         p.css = p.attr = "";
59686                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59687                         if(p.value == undefined || p.value === "") {
59688                             p.value = "&#160;";
59689                         }
59690                         //Roo.log(c);
59691                          if(c.has_editor){
59692                             p.css += ' x-grid-editable-cell';
59693                         }
59694                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59695                             p.css += ' x-grid-dirty-cell' 
59696                         }
59697                         
59698                         var markup = ct.apply(p);
59699                         if(!c.locked){
59700                             cb[cb.length] = markup;
59701                         }else{
59702                             lcb[lcb.length] = markup;
59703                         }
59704                     }
59705                     var alt = [];
59706                     if(stripe && ((rowIndex+1) % 2 == 0)){
59707                         alt.push( "x-grid-row-alt");
59708                     }
59709                     if(r.dirty){
59710                         alt.push(" x-grid-dirty-row");
59711                     }
59712                     rp.cells = lcb;
59713                     if(this.getRowClass){
59714                         alt.push( this.getRowClass(r, rowIndex));
59715                     }
59716                     if (hasListener) {
59717                         rowcfg = {
59718                              
59719                             record: r,
59720                             rowIndex : rowIndex,
59721                             rowClass : ''
59722                         };
59723                         this.grid.fireEvent('rowclass', this, rowcfg);
59724                         alt.push(rowcfg.rowClass);
59725                     }
59726                     
59727                     rp.alt = alt.join(" ");
59728                     rp.cells = lcb.join("");
59729                     lbuf[lbuf.length] = rt.apply(rp);
59730                     rp.cells = cb.join("");
59731                     buf[buf.length] =  rt.apply(rp);
59732                 }
59733                 return [lbuf.join(""), buf.join("")];
59734             },
59735
59736     renderBody : function(){
59737         var markup = this.renderRows();
59738         var bt = this.templates.body;
59739         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59740     },
59741
59742     /**
59743      * Refreshes the grid
59744      * @param {Boolean} headersToo
59745      */
59746     refresh : function(headersToo){
59747         this.fireEvent("beforerefresh", this);
59748         this.grid.stopEditing();
59749         var result = this.renderBody();
59750         this.lockedBody.update(result[0]);
59751         this.mainBody.update(result[1]);
59752         if(headersToo === true){
59753             this.updateHeaders();
59754             this.updateColumns();
59755             this.updateSplitters();
59756             this.updateHeaderSortState();
59757         }
59758         this.syncRowHeights();
59759         this.layout();
59760         this.fireEvent("refresh", this);
59761     },
59762
59763     handleColumnMove : function(cm, oldIndex, newIndex){
59764         this.indexMap = null;
59765         var s = this.getScrollState();
59766         this.refresh(true);
59767         this.restoreScroll(s);
59768         this.afterMove(newIndex);
59769     },
59770
59771     afterMove : function(colIndex){
59772         if(this.enableMoveAnim && Roo.enableFx){
59773             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59774         }
59775         // if multisort - fix sortOrder, and reload..
59776         if (this.grid.dataSource.multiSort) {
59777             // the we can call sort again..
59778             var dm = this.grid.dataSource;
59779             var cm = this.grid.colModel;
59780             var so = [];
59781             for(var i = 0; i < cm.config.length; i++ ) {
59782                 
59783                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59784                     continue; // dont' bother, it's not in sort list or being set.
59785                 }
59786                 
59787                 so.push(cm.config[i].dataIndex);
59788             };
59789             dm.sortOrder = so;
59790             dm.load(dm.lastOptions);
59791             
59792             
59793         }
59794         
59795     },
59796
59797     updateCell : function(dm, rowIndex, dataIndex){
59798         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59799         if(typeof colIndex == "undefined"){ // not present in grid
59800             return;
59801         }
59802         var cm = this.grid.colModel;
59803         var cell = this.getCell(rowIndex, colIndex);
59804         var cellText = this.getCellText(rowIndex, colIndex);
59805
59806         var p = {
59807             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59808             id : cm.getColumnId(colIndex),
59809             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59810         };
59811         var renderer = cm.getRenderer(colIndex);
59812         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59813         if(typeof val == "undefined" || val === "") {
59814             val = "&#160;";
59815         }
59816         cellText.innerHTML = val;
59817         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59818         this.syncRowHeights(rowIndex, rowIndex);
59819     },
59820
59821     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59822         var maxWidth = 0;
59823         if(this.grid.autoSizeHeaders){
59824             var h = this.getHeaderCellMeasure(colIndex);
59825             maxWidth = Math.max(maxWidth, h.scrollWidth);
59826         }
59827         var tb, index;
59828         if(this.cm.isLocked(colIndex)){
59829             tb = this.getLockedTable();
59830             index = colIndex;
59831         }else{
59832             tb = this.getBodyTable();
59833             index = colIndex - this.cm.getLockedCount();
59834         }
59835         if(tb && tb.rows){
59836             var rows = tb.rows;
59837             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59838             for(var i = 0; i < stopIndex; i++){
59839                 var cell = rows[i].childNodes[index].firstChild;
59840                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59841             }
59842         }
59843         return maxWidth + /*margin for error in IE*/ 5;
59844     },
59845     /**
59846      * Autofit a column to its content.
59847      * @param {Number} colIndex
59848      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59849      */
59850      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59851          if(this.cm.isHidden(colIndex)){
59852              return; // can't calc a hidden column
59853          }
59854         if(forceMinSize){
59855             var cid = this.cm.getColumnId(colIndex);
59856             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59857            if(this.grid.autoSizeHeaders){
59858                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59859            }
59860         }
59861         var newWidth = this.calcColumnWidth(colIndex);
59862         this.cm.setColumnWidth(colIndex,
59863             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59864         if(!suppressEvent){
59865             this.grid.fireEvent("columnresize", colIndex, newWidth);
59866         }
59867     },
59868
59869     /**
59870      * Autofits all columns to their content and then expands to fit any extra space in the grid
59871      */
59872      autoSizeColumns : function(){
59873         var cm = this.grid.colModel;
59874         var colCount = cm.getColumnCount();
59875         for(var i = 0; i < colCount; i++){
59876             this.autoSizeColumn(i, true, true);
59877         }
59878         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59879             this.fitColumns();
59880         }else{
59881             this.updateColumns();
59882             this.layout();
59883         }
59884     },
59885
59886     /**
59887      * Autofits all columns to the grid's width proportionate with their current size
59888      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59889      */
59890     fitColumns : function(reserveScrollSpace){
59891         var cm = this.grid.colModel;
59892         var colCount = cm.getColumnCount();
59893         var cols = [];
59894         var width = 0;
59895         var i, w;
59896         for (i = 0; i < colCount; i++){
59897             if(!cm.isHidden(i) && !cm.isFixed(i)){
59898                 w = cm.getColumnWidth(i);
59899                 cols.push(i);
59900                 cols.push(w);
59901                 width += w;
59902             }
59903         }
59904         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59905         if(reserveScrollSpace){
59906             avail -= 17;
59907         }
59908         var frac = (avail - cm.getTotalWidth())/width;
59909         while (cols.length){
59910             w = cols.pop();
59911             i = cols.pop();
59912             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59913         }
59914         this.updateColumns();
59915         this.layout();
59916     },
59917
59918     onRowSelect : function(rowIndex){
59919         var row = this.getRowComposite(rowIndex);
59920         row.addClass("x-grid-row-selected");
59921     },
59922
59923     onRowDeselect : function(rowIndex){
59924         var row = this.getRowComposite(rowIndex);
59925         row.removeClass("x-grid-row-selected");
59926     },
59927
59928     onCellSelect : function(row, col){
59929         var cell = this.getCell(row, col);
59930         if(cell){
59931             Roo.fly(cell).addClass("x-grid-cell-selected");
59932         }
59933     },
59934
59935     onCellDeselect : function(row, col){
59936         var cell = this.getCell(row, col);
59937         if(cell){
59938             Roo.fly(cell).removeClass("x-grid-cell-selected");
59939         }
59940     },
59941
59942     updateHeaderSortState : function(){
59943         
59944         // sort state can be single { field: xxx, direction : yyy}
59945         // or   { xxx=>ASC , yyy : DESC ..... }
59946         
59947         var mstate = {};
59948         if (!this.ds.multiSort) { 
59949             var state = this.ds.getSortState();
59950             if(!state){
59951                 return;
59952             }
59953             mstate[state.field] = state.direction;
59954             // FIXME... - this is not used here.. but might be elsewhere..
59955             this.sortState = state;
59956             
59957         } else {
59958             mstate = this.ds.sortToggle;
59959         }
59960         //remove existing sort classes..
59961         
59962         var sc = this.sortClasses;
59963         var hds = this.el.select(this.headerSelector).removeClass(sc);
59964         
59965         for(var f in mstate) {
59966         
59967             var sortColumn = this.cm.findColumnIndex(f);
59968             
59969             if(sortColumn != -1){
59970                 var sortDir = mstate[f];        
59971                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59972             }
59973         }
59974         
59975          
59976         
59977     },
59978
59979
59980     handleHeaderClick : function(g, index,e){
59981         
59982         Roo.log("header click");
59983         
59984         if (Roo.isTouch) {
59985             // touch events on header are handled by context
59986             this.handleHdCtx(g,index,e);
59987             return;
59988         }
59989         
59990         
59991         if(this.headersDisabled){
59992             return;
59993         }
59994         var dm = g.dataSource, cm = g.colModel;
59995         if(!cm.isSortable(index)){
59996             return;
59997         }
59998         g.stopEditing();
59999         
60000         if (dm.multiSort) {
60001             // update the sortOrder
60002             var so = [];
60003             for(var i = 0; i < cm.config.length; i++ ) {
60004                 
60005                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60006                     continue; // dont' bother, it's not in sort list or being set.
60007                 }
60008                 
60009                 so.push(cm.config[i].dataIndex);
60010             };
60011             dm.sortOrder = so;
60012         }
60013         
60014         
60015         dm.sort(cm.getDataIndex(index));
60016     },
60017
60018
60019     destroy : function(){
60020         if(this.colMenu){
60021             this.colMenu.removeAll();
60022             Roo.menu.MenuMgr.unregister(this.colMenu);
60023             this.colMenu.getEl().remove();
60024             delete this.colMenu;
60025         }
60026         if(this.hmenu){
60027             this.hmenu.removeAll();
60028             Roo.menu.MenuMgr.unregister(this.hmenu);
60029             this.hmenu.getEl().remove();
60030             delete this.hmenu;
60031         }
60032         if(this.grid.enableColumnMove){
60033             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60034             if(dds){
60035                 for(var dd in dds){
60036                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60037                         var elid = dds[dd].dragElId;
60038                         dds[dd].unreg();
60039                         Roo.get(elid).remove();
60040                     } else if(dds[dd].config.isTarget){
60041                         dds[dd].proxyTop.remove();
60042                         dds[dd].proxyBottom.remove();
60043                         dds[dd].unreg();
60044                     }
60045                     if(Roo.dd.DDM.locationCache[dd]){
60046                         delete Roo.dd.DDM.locationCache[dd];
60047                     }
60048                 }
60049                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60050             }
60051         }
60052         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60053         this.bind(null, null);
60054         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60055     },
60056
60057     handleLockChange : function(){
60058         this.refresh(true);
60059     },
60060
60061     onDenyColumnLock : function(){
60062
60063     },
60064
60065     onDenyColumnHide : function(){
60066
60067     },
60068
60069     handleHdMenuClick : function(item){
60070         var index = this.hdCtxIndex;
60071         var cm = this.cm, ds = this.ds;
60072         switch(item.id){
60073             case "asc":
60074                 ds.sort(cm.getDataIndex(index), "ASC");
60075                 break;
60076             case "desc":
60077                 ds.sort(cm.getDataIndex(index), "DESC");
60078                 break;
60079             case "lock":
60080                 var lc = cm.getLockedCount();
60081                 if(cm.getColumnCount(true) <= lc+1){
60082                     this.onDenyColumnLock();
60083                     return;
60084                 }
60085                 if(lc != index){
60086                     cm.setLocked(index, true, true);
60087                     cm.moveColumn(index, lc);
60088                     this.grid.fireEvent("columnmove", index, lc);
60089                 }else{
60090                     cm.setLocked(index, true);
60091                 }
60092             break;
60093             case "unlock":
60094                 var lc = cm.getLockedCount();
60095                 if((lc-1) != index){
60096                     cm.setLocked(index, false, true);
60097                     cm.moveColumn(index, lc-1);
60098                     this.grid.fireEvent("columnmove", index, lc-1);
60099                 }else{
60100                     cm.setLocked(index, false);
60101                 }
60102             break;
60103             case 'wider': // used to expand cols on touch..
60104             case 'narrow':
60105                 var cw = cm.getColumnWidth(index);
60106                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60107                 cw = Math.max(0, cw);
60108                 cw = Math.min(cw,4000);
60109                 cm.setColumnWidth(index, cw);
60110                 break;
60111                 
60112             default:
60113                 index = cm.getIndexById(item.id.substr(4));
60114                 if(index != -1){
60115                     if(item.checked && cm.getColumnCount(true) <= 1){
60116                         this.onDenyColumnHide();
60117                         return false;
60118                     }
60119                     cm.setHidden(index, item.checked);
60120                 }
60121         }
60122         return true;
60123     },
60124
60125     beforeColMenuShow : function(){
60126         var cm = this.cm,  colCount = cm.getColumnCount();
60127         this.colMenu.removeAll();
60128         for(var i = 0; i < colCount; i++){
60129             this.colMenu.add(new Roo.menu.CheckItem({
60130                 id: "col-"+cm.getColumnId(i),
60131                 text: cm.getColumnHeader(i),
60132                 checked: !cm.isHidden(i),
60133                 hideOnClick:false
60134             }));
60135         }
60136     },
60137
60138     handleHdCtx : function(g, index, e){
60139         e.stopEvent();
60140         var hd = this.getHeaderCell(index);
60141         this.hdCtxIndex = index;
60142         var ms = this.hmenu.items, cm = this.cm;
60143         ms.get("asc").setDisabled(!cm.isSortable(index));
60144         ms.get("desc").setDisabled(!cm.isSortable(index));
60145         if(this.grid.enableColLock !== false){
60146             ms.get("lock").setDisabled(cm.isLocked(index));
60147             ms.get("unlock").setDisabled(!cm.isLocked(index));
60148         }
60149         this.hmenu.show(hd, "tl-bl");
60150     },
60151
60152     handleHdOver : function(e){
60153         var hd = this.findHeaderCell(e.getTarget());
60154         if(hd && !this.headersDisabled){
60155             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60156                this.fly(hd).addClass("x-grid-hd-over");
60157             }
60158         }
60159     },
60160
60161     handleHdOut : function(e){
60162         var hd = this.findHeaderCell(e.getTarget());
60163         if(hd){
60164             this.fly(hd).removeClass("x-grid-hd-over");
60165         }
60166     },
60167
60168     handleSplitDblClick : function(e, t){
60169         var i = this.getCellIndex(t);
60170         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60171             this.autoSizeColumn(i, true);
60172             this.layout();
60173         }
60174     },
60175
60176     render : function(){
60177
60178         var cm = this.cm;
60179         var colCount = cm.getColumnCount();
60180
60181         if(this.grid.monitorWindowResize === true){
60182             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60183         }
60184         var header = this.renderHeaders();
60185         var body = this.templates.body.apply({rows:""});
60186         var html = this.templates.master.apply({
60187             lockedBody: body,
60188             body: body,
60189             lockedHeader: header[0],
60190             header: header[1]
60191         });
60192
60193         //this.updateColumns();
60194
60195         this.grid.getGridEl().dom.innerHTML = html;
60196
60197         this.initElements();
60198         
60199         // a kludge to fix the random scolling effect in webkit
60200         this.el.on("scroll", function() {
60201             this.el.dom.scrollTop=0; // hopefully not recursive..
60202         },this);
60203
60204         this.scroller.on("scroll", this.handleScroll, this);
60205         this.lockedBody.on("mousewheel", this.handleWheel, this);
60206         this.mainBody.on("mousewheel", this.handleWheel, this);
60207
60208         this.mainHd.on("mouseover", this.handleHdOver, this);
60209         this.mainHd.on("mouseout", this.handleHdOut, this);
60210         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60211                 {delegate: "."+this.splitClass});
60212
60213         this.lockedHd.on("mouseover", this.handleHdOver, this);
60214         this.lockedHd.on("mouseout", this.handleHdOut, this);
60215         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60216                 {delegate: "."+this.splitClass});
60217
60218         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60219             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60220         }
60221
60222         this.updateSplitters();
60223
60224         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60225             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60226             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60227         }
60228
60229         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60230             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60231             this.hmenu.add(
60232                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60233                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60234             );
60235             if(this.grid.enableColLock !== false){
60236                 this.hmenu.add('-',
60237                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60238                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60239                 );
60240             }
60241             if (Roo.isTouch) {
60242                  this.hmenu.add('-',
60243                     {id:"wider", text: this.columnsWiderText},
60244                     {id:"narrow", text: this.columnsNarrowText }
60245                 );
60246                 
60247                  
60248             }
60249             
60250             if(this.grid.enableColumnHide !== false){
60251
60252                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60253                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60254                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60255
60256                 this.hmenu.add('-',
60257                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60258                 );
60259             }
60260             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60261
60262             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60263         }
60264
60265         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60266             this.dd = new Roo.grid.GridDragZone(this.grid, {
60267                 ddGroup : this.grid.ddGroup || 'GridDD'
60268             });
60269             
60270         }
60271
60272         /*
60273         for(var i = 0; i < colCount; i++){
60274             if(cm.isHidden(i)){
60275                 this.hideColumn(i);
60276             }
60277             if(cm.config[i].align){
60278                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60279                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60280             }
60281         }*/
60282         
60283         this.updateHeaderSortState();
60284
60285         this.beforeInitialResize();
60286         this.layout(true);
60287
60288         // two part rendering gives faster view to the user
60289         this.renderPhase2.defer(1, this);
60290     },
60291
60292     renderPhase2 : function(){
60293         // render the rows now
60294         this.refresh();
60295         if(this.grid.autoSizeColumns){
60296             this.autoSizeColumns();
60297         }
60298     },
60299
60300     beforeInitialResize : function(){
60301
60302     },
60303
60304     onColumnSplitterMoved : function(i, w){
60305         this.userResized = true;
60306         var cm = this.grid.colModel;
60307         cm.setColumnWidth(i, w, true);
60308         var cid = cm.getColumnId(i);
60309         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60310         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60311         this.updateSplitters();
60312         this.layout();
60313         this.grid.fireEvent("columnresize", i, w);
60314     },
60315
60316     syncRowHeights : function(startIndex, endIndex){
60317         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60318             startIndex = startIndex || 0;
60319             var mrows = this.getBodyTable().rows;
60320             var lrows = this.getLockedTable().rows;
60321             var len = mrows.length-1;
60322             endIndex = Math.min(endIndex || len, len);
60323             for(var i = startIndex; i <= endIndex; i++){
60324                 var m = mrows[i], l = lrows[i];
60325                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60326                 m.style.height = l.style.height = h + "px";
60327             }
60328         }
60329     },
60330
60331     layout : function(initialRender, is2ndPass)
60332     {
60333         var g = this.grid;
60334         var auto = g.autoHeight;
60335         var scrollOffset = 16;
60336         var c = g.getGridEl(), cm = this.cm,
60337                 expandCol = g.autoExpandColumn,
60338                 gv = this;
60339         //c.beginMeasure();
60340
60341         if(!c.dom.offsetWidth){ // display:none?
60342             if(initialRender){
60343                 this.lockedWrap.show();
60344                 this.mainWrap.show();
60345             }
60346             return;
60347         }
60348
60349         var hasLock = this.cm.isLocked(0);
60350
60351         var tbh = this.headerPanel.getHeight();
60352         var bbh = this.footerPanel.getHeight();
60353
60354         if(auto){
60355             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60356             var newHeight = ch + c.getBorderWidth("tb");
60357             if(g.maxHeight){
60358                 newHeight = Math.min(g.maxHeight, newHeight);
60359             }
60360             c.setHeight(newHeight);
60361         }
60362
60363         if(g.autoWidth){
60364             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60365         }
60366
60367         var s = this.scroller;
60368
60369         var csize = c.getSize(true);
60370
60371         this.el.setSize(csize.width, csize.height);
60372
60373         this.headerPanel.setWidth(csize.width);
60374         this.footerPanel.setWidth(csize.width);
60375
60376         var hdHeight = this.mainHd.getHeight();
60377         var vw = csize.width;
60378         var vh = csize.height - (tbh + bbh);
60379
60380         s.setSize(vw, vh);
60381
60382         var bt = this.getBodyTable();
60383         
60384         if(cm.getLockedCount() == cm.config.length){
60385             bt = this.getLockedTable();
60386         }
60387         
60388         var ltWidth = hasLock ?
60389                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60390
60391         var scrollHeight = bt.offsetHeight;
60392         var scrollWidth = ltWidth + bt.offsetWidth;
60393         var vscroll = false, hscroll = false;
60394
60395         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60396
60397         var lw = this.lockedWrap, mw = this.mainWrap;
60398         var lb = this.lockedBody, mb = this.mainBody;
60399
60400         setTimeout(function(){
60401             var t = s.dom.offsetTop;
60402             var w = s.dom.clientWidth,
60403                 h = s.dom.clientHeight;
60404
60405             lw.setTop(t);
60406             lw.setSize(ltWidth, h);
60407
60408             mw.setLeftTop(ltWidth, t);
60409             mw.setSize(w-ltWidth, h);
60410
60411             lb.setHeight(h-hdHeight);
60412             mb.setHeight(h-hdHeight);
60413
60414             if(is2ndPass !== true && !gv.userResized && expandCol){
60415                 // high speed resize without full column calculation
60416                 
60417                 var ci = cm.getIndexById(expandCol);
60418                 if (ci < 0) {
60419                     ci = cm.findColumnIndex(expandCol);
60420                 }
60421                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60422                 var expandId = cm.getColumnId(ci);
60423                 var  tw = cm.getTotalWidth(false);
60424                 var currentWidth = cm.getColumnWidth(ci);
60425                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60426                 if(currentWidth != cw){
60427                     cm.setColumnWidth(ci, cw, true);
60428                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60429                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60430                     gv.updateSplitters();
60431                     gv.layout(false, true);
60432                 }
60433             }
60434
60435             if(initialRender){
60436                 lw.show();
60437                 mw.show();
60438             }
60439             //c.endMeasure();
60440         }, 10);
60441     },
60442
60443     onWindowResize : function(){
60444         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60445             return;
60446         }
60447         this.layout();
60448     },
60449
60450     appendFooter : function(parentEl){
60451         return null;
60452     },
60453
60454     sortAscText : "Sort Ascending",
60455     sortDescText : "Sort Descending",
60456     lockText : "Lock Column",
60457     unlockText : "Unlock Column",
60458     columnsText : "Columns",
60459  
60460     columnsWiderText : "Wider",
60461     columnsNarrowText : "Thinner"
60462 });
60463
60464
60465 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60466     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60467     this.proxy.el.addClass('x-grid3-col-dd');
60468 };
60469
60470 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60471     handleMouseDown : function(e){
60472
60473     },
60474
60475     callHandleMouseDown : function(e){
60476         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60477     }
60478 });
60479 /*
60480  * Based on:
60481  * Ext JS Library 1.1.1
60482  * Copyright(c) 2006-2007, Ext JS, LLC.
60483  *
60484  * Originally Released Under LGPL - original licence link has changed is not relivant.
60485  *
60486  * Fork - LGPL
60487  * <script type="text/javascript">
60488  */
60489  /**
60490  * @extends Roo.dd.DDProxy
60491  * @class Roo.grid.SplitDragZone
60492  * Support for Column Header resizing
60493  * @constructor
60494  * @param {Object} config
60495  */
60496 // private
60497 // This is a support class used internally by the Grid components
60498 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60499     this.grid = grid;
60500     this.view = grid.getView();
60501     this.proxy = this.view.resizeProxy;
60502     Roo.grid.SplitDragZone.superclass.constructor.call(
60503         this,
60504         hd, // ID
60505         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60506         {  // CONFIG
60507             dragElId : Roo.id(this.proxy.dom),
60508             resizeFrame:false
60509         }
60510     );
60511     
60512     this.setHandleElId(Roo.id(hd));
60513     if (hd2 !== false) {
60514         this.setOuterHandleElId(Roo.id(hd2));
60515     }
60516     
60517     this.scroll = false;
60518 };
60519 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60520     fly: Roo.Element.fly,
60521
60522     b4StartDrag : function(x, y){
60523         this.view.headersDisabled = true;
60524         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60525                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60526         );
60527         this.proxy.setHeight(h);
60528         
60529         // for old system colWidth really stored the actual width?
60530         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60531         // which in reality did not work.. - it worked only for fixed sizes
60532         // for resizable we need to use actual sizes.
60533         var w = this.cm.getColumnWidth(this.cellIndex);
60534         if (!this.view.mainWrap) {
60535             // bootstrap.
60536             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60537         }
60538         
60539         
60540         
60541         // this was w-this.grid.minColumnWidth;
60542         // doesnt really make sense? - w = thie curren width or the rendered one?
60543         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60544         this.resetConstraints();
60545         this.setXConstraint(minw, 1000);
60546         this.setYConstraint(0, 0);
60547         this.minX = x - minw;
60548         this.maxX = x + 1000;
60549         this.startPos = x;
60550         if (!this.view.mainWrap) { // this is Bootstrap code..
60551             this.getDragEl().style.display='block';
60552         }
60553         
60554         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60555     },
60556
60557
60558     handleMouseDown : function(e){
60559         ev = Roo.EventObject.setEvent(e);
60560         var t = this.fly(ev.getTarget());
60561         if(t.hasClass("x-grid-split")){
60562             this.cellIndex = this.view.getCellIndex(t.dom);
60563             this.split = t.dom;
60564             this.cm = this.grid.colModel;
60565             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60566                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60567             }
60568         }
60569     },
60570
60571     endDrag : function(e){
60572         this.view.headersDisabled = false;
60573         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60574         var diff = endX - this.startPos;
60575         // 
60576         var w = this.cm.getColumnWidth(this.cellIndex);
60577         if (!this.view.mainWrap) {
60578             w = 0;
60579         }
60580         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60581     },
60582
60583     autoOffset : function(){
60584         this.setDelta(0,0);
60585     }
60586 });/*
60587  * Based on:
60588  * Ext JS Library 1.1.1
60589  * Copyright(c) 2006-2007, Ext JS, LLC.
60590  *
60591  * Originally Released Under LGPL - original licence link has changed is not relivant.
60592  *
60593  * Fork - LGPL
60594  * <script type="text/javascript">
60595  */
60596  
60597 // private
60598 // This is a support class used internally by the Grid components
60599 Roo.grid.GridDragZone = function(grid, config){
60600     this.view = grid.getView();
60601     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60602     if(this.view.lockedBody){
60603         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60604         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60605     }
60606     this.scroll = false;
60607     this.grid = grid;
60608     this.ddel = document.createElement('div');
60609     this.ddel.className = 'x-grid-dd-wrap';
60610 };
60611
60612 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60613     ddGroup : "GridDD",
60614
60615     getDragData : function(e){
60616         var t = Roo.lib.Event.getTarget(e);
60617         var rowIndex = this.view.findRowIndex(t);
60618         var sm = this.grid.selModel;
60619             
60620         //Roo.log(rowIndex);
60621         
60622         if (sm.getSelectedCell) {
60623             // cell selection..
60624             if (!sm.getSelectedCell()) {
60625                 return false;
60626             }
60627             if (rowIndex != sm.getSelectedCell()[0]) {
60628                 return false;
60629             }
60630         
60631         }
60632         if (sm.getSelections && sm.getSelections().length < 1) {
60633             return false;
60634         }
60635         
60636         
60637         // before it used to all dragging of unseleted... - now we dont do that.
60638         if(rowIndex !== false){
60639             
60640             // if editorgrid.. 
60641             
60642             
60643             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60644                
60645             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60646               //  
60647             //}
60648             if (e.hasModifier()){
60649                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60650             }
60651             
60652             Roo.log("getDragData");
60653             
60654             return {
60655                 grid: this.grid,
60656                 ddel: this.ddel,
60657                 rowIndex: rowIndex,
60658                 selections: sm.getSelections ? sm.getSelections() : (
60659                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60660             };
60661         }
60662         return false;
60663     },
60664     
60665     
60666     onInitDrag : function(e){
60667         var data = this.dragData;
60668         this.ddel.innerHTML = this.grid.getDragDropText();
60669         this.proxy.update(this.ddel);
60670         // fire start drag?
60671     },
60672
60673     afterRepair : function(){
60674         this.dragging = false;
60675     },
60676
60677     getRepairXY : function(e, data){
60678         return false;
60679     },
60680
60681     onEndDrag : function(data, e){
60682         // fire end drag?
60683     },
60684
60685     onValidDrop : function(dd, e, id){
60686         // fire drag drop?
60687         this.hideProxy();
60688     },
60689
60690     beforeInvalidDrop : function(e, id){
60691
60692     }
60693 });/*
60694  * Based on:
60695  * Ext JS Library 1.1.1
60696  * Copyright(c) 2006-2007, Ext JS, LLC.
60697  *
60698  * Originally Released Under LGPL - original licence link has changed is not relivant.
60699  *
60700  * Fork - LGPL
60701  * <script type="text/javascript">
60702  */
60703  
60704
60705 /**
60706  * @class Roo.grid.ColumnModel
60707  * @extends Roo.util.Observable
60708  * This is the default implementation of a ColumnModel used by the Grid. It defines
60709  * the columns in the grid.
60710  * <br>Usage:<br>
60711  <pre><code>
60712  var colModel = new Roo.grid.ColumnModel([
60713         {header: "Ticker", width: 60, sortable: true, locked: true},
60714         {header: "Company Name", width: 150, sortable: true},
60715         {header: "Market Cap.", width: 100, sortable: true},
60716         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60717         {header: "Employees", width: 100, sortable: true, resizable: false}
60718  ]);
60719  </code></pre>
60720  * <p>
60721  
60722  * The config options listed for this class are options which may appear in each
60723  * individual column definition.
60724  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60725  * @constructor
60726  * @param {Object} config An Array of column config objects. See this class's
60727  * config objects for details.
60728 */
60729 Roo.grid.ColumnModel = function(config){
60730         /**
60731      * The config passed into the constructor
60732      */
60733     this.config = []; //config;
60734     this.lookup = {};
60735
60736     // if no id, create one
60737     // if the column does not have a dataIndex mapping,
60738     // map it to the order it is in the config
60739     for(var i = 0, len = config.length; i < len; i++){
60740         this.addColumn(config[i]);
60741         
60742     }
60743
60744     /**
60745      * The width of columns which have no width specified (defaults to 100)
60746      * @type Number
60747      */
60748     this.defaultWidth = 100;
60749
60750     /**
60751      * Default sortable of columns which have no sortable specified (defaults to false)
60752      * @type Boolean
60753      */
60754     this.defaultSortable = false;
60755
60756     this.addEvents({
60757         /**
60758              * @event widthchange
60759              * Fires when the width of a column changes.
60760              * @param {ColumnModel} this
60761              * @param {Number} columnIndex The column index
60762              * @param {Number} newWidth The new width
60763              */
60764             "widthchange": true,
60765         /**
60766              * @event headerchange
60767              * Fires when the text of a header changes.
60768              * @param {ColumnModel} this
60769              * @param {Number} columnIndex The column index
60770              * @param {Number} newText The new header text
60771              */
60772             "headerchange": true,
60773         /**
60774              * @event hiddenchange
60775              * Fires when a column is hidden or "unhidden".
60776              * @param {ColumnModel} this
60777              * @param {Number} columnIndex The column index
60778              * @param {Boolean} hidden true if hidden, false otherwise
60779              */
60780             "hiddenchange": true,
60781             /**
60782          * @event columnmoved
60783          * Fires when a column is moved.
60784          * @param {ColumnModel} this
60785          * @param {Number} oldIndex
60786          * @param {Number} newIndex
60787          */
60788         "columnmoved" : true,
60789         /**
60790          * @event columlockchange
60791          * Fires when a column's locked state is changed
60792          * @param {ColumnModel} this
60793          * @param {Number} colIndex
60794          * @param {Boolean} locked true if locked
60795          */
60796         "columnlockchange" : true
60797     });
60798     Roo.grid.ColumnModel.superclass.constructor.call(this);
60799 };
60800 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60801     /**
60802      * @cfg {String} header The header text to display in the Grid view.
60803      */
60804         /**
60805      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60806      */
60807         /**
60808      * @cfg {String} smHeader Header at Bootsrap Small width
60809      */
60810         /**
60811      * @cfg {String} mdHeader Header at Bootsrap Medium width
60812      */
60813         /**
60814      * @cfg {String} lgHeader Header at Bootsrap Large width
60815      */
60816         /**
60817      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60818      */
60819     /**
60820      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60821      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60822      * specified, the column's index is used as an index into the Record's data Array.
60823      */
60824     /**
60825      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60826      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60827      */
60828     /**
60829      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60830      * Defaults to the value of the {@link #defaultSortable} property.
60831      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60832      */
60833     /**
60834      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60835      */
60836     /**
60837      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60838      */
60839     /**
60840      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60841      */
60842     /**
60843      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60844      */
60845     /**
60846      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60847      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60848      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60849      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60850      */
60851        /**
60852      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60853      */
60854     /**
60855      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60856      */
60857     /**
60858      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60859      */
60860     /**
60861      * @cfg {String} cursor (Optional)
60862      */
60863     /**
60864      * @cfg {String} tooltip (Optional)
60865      */
60866     /**
60867      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60868      */
60869     /**
60870      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60871      */
60872     /**
60873      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60874      */
60875     /**
60876      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60877      */
60878         /**
60879      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60880      */
60881     /**
60882      * Returns the id of the column at the specified index.
60883      * @param {Number} index The column index
60884      * @return {String} the id
60885      */
60886     getColumnId : function(index){
60887         return this.config[index].id;
60888     },
60889
60890     /**
60891      * Returns the column for a specified id.
60892      * @param {String} id The column id
60893      * @return {Object} the column
60894      */
60895     getColumnById : function(id){
60896         return this.lookup[id];
60897     },
60898
60899     
60900     /**
60901      * Returns the column Object for a specified dataIndex.
60902      * @param {String} dataIndex The column dataIndex
60903      * @return {Object|Boolean} the column or false if not found
60904      */
60905     getColumnByDataIndex: function(dataIndex){
60906         var index = this.findColumnIndex(dataIndex);
60907         return index > -1 ? this.config[index] : false;
60908     },
60909     
60910     /**
60911      * Returns the index for a specified column id.
60912      * @param {String} id The column id
60913      * @return {Number} the index, or -1 if not found
60914      */
60915     getIndexById : function(id){
60916         for(var i = 0, len = this.config.length; i < len; i++){
60917             if(this.config[i].id == id){
60918                 return i;
60919             }
60920         }
60921         return -1;
60922     },
60923     
60924     /**
60925      * Returns the index for a specified column dataIndex.
60926      * @param {String} dataIndex The column dataIndex
60927      * @return {Number} the index, or -1 if not found
60928      */
60929     
60930     findColumnIndex : function(dataIndex){
60931         for(var i = 0, len = this.config.length; i < len; i++){
60932             if(this.config[i].dataIndex == dataIndex){
60933                 return i;
60934             }
60935         }
60936         return -1;
60937     },
60938     
60939     
60940     moveColumn : function(oldIndex, newIndex){
60941         var c = this.config[oldIndex];
60942         this.config.splice(oldIndex, 1);
60943         this.config.splice(newIndex, 0, c);
60944         this.dataMap = null;
60945         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60946     },
60947
60948     isLocked : function(colIndex){
60949         return this.config[colIndex].locked === true;
60950     },
60951
60952     setLocked : function(colIndex, value, suppressEvent){
60953         if(this.isLocked(colIndex) == value){
60954             return;
60955         }
60956         this.config[colIndex].locked = value;
60957         if(!suppressEvent){
60958             this.fireEvent("columnlockchange", this, colIndex, value);
60959         }
60960     },
60961
60962     getTotalLockedWidth : function(){
60963         var totalWidth = 0;
60964         for(var i = 0; i < this.config.length; i++){
60965             if(this.isLocked(i) && !this.isHidden(i)){
60966                 this.totalWidth += this.getColumnWidth(i);
60967             }
60968         }
60969         return totalWidth;
60970     },
60971
60972     getLockedCount : function(){
60973         for(var i = 0, len = this.config.length; i < len; i++){
60974             if(!this.isLocked(i)){
60975                 return i;
60976             }
60977         }
60978         
60979         return this.config.length;
60980     },
60981
60982     /**
60983      * Returns the number of columns.
60984      * @return {Number}
60985      */
60986     getColumnCount : function(visibleOnly){
60987         if(visibleOnly === true){
60988             var c = 0;
60989             for(var i = 0, len = this.config.length; i < len; i++){
60990                 if(!this.isHidden(i)){
60991                     c++;
60992                 }
60993             }
60994             return c;
60995         }
60996         return this.config.length;
60997     },
60998
60999     /**
61000      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61001      * @param {Function} fn
61002      * @param {Object} scope (optional)
61003      * @return {Array} result
61004      */
61005     getColumnsBy : function(fn, scope){
61006         var r = [];
61007         for(var i = 0, len = this.config.length; i < len; i++){
61008             var c = this.config[i];
61009             if(fn.call(scope||this, c, i) === true){
61010                 r[r.length] = c;
61011             }
61012         }
61013         return r;
61014     },
61015
61016     /**
61017      * Returns true if the specified column is sortable.
61018      * @param {Number} col The column index
61019      * @return {Boolean}
61020      */
61021     isSortable : function(col){
61022         if(typeof this.config[col].sortable == "undefined"){
61023             return this.defaultSortable;
61024         }
61025         return this.config[col].sortable;
61026     },
61027
61028     /**
61029      * Returns the rendering (formatting) function defined for the column.
61030      * @param {Number} col The column index.
61031      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61032      */
61033     getRenderer : function(col){
61034         if(!this.config[col].renderer){
61035             return Roo.grid.ColumnModel.defaultRenderer;
61036         }
61037         return this.config[col].renderer;
61038     },
61039
61040     /**
61041      * Sets the rendering (formatting) function for a column.
61042      * @param {Number} col The column index
61043      * @param {Function} fn The function to use to process the cell's raw data
61044      * to return HTML markup for the grid view. The render function is called with
61045      * the following parameters:<ul>
61046      * <li>Data value.</li>
61047      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61048      * <li>css A CSS style string to apply to the table cell.</li>
61049      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61050      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61051      * <li>Row index</li>
61052      * <li>Column index</li>
61053      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61054      */
61055     setRenderer : function(col, fn){
61056         this.config[col].renderer = fn;
61057     },
61058
61059     /**
61060      * Returns the width for the specified column.
61061      * @param {Number} col The column index
61062      * @param (optional) {String} gridSize bootstrap width size.
61063      * @return {Number}
61064      */
61065     getColumnWidth : function(col, gridSize)
61066         {
61067                 var cfg = this.config[col];
61068                 
61069                 if (typeof(gridSize) == 'undefined') {
61070                         return cfg.width * 1 || this.defaultWidth;
61071                 }
61072                 if (gridSize === false) { // if we set it..
61073                         return cfg.width || false;
61074                 }
61075                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61076                 
61077                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61078                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61079                                 continue;
61080                         }
61081                         return cfg[ sizes[i] ];
61082                 }
61083                 return 1;
61084                 
61085     },
61086
61087     /**
61088      * Sets the width for a column.
61089      * @param {Number} col The column index
61090      * @param {Number} width The new width
61091      */
61092     setColumnWidth : function(col, width, suppressEvent){
61093         this.config[col].width = width;
61094         this.totalWidth = null;
61095         if(!suppressEvent){
61096              this.fireEvent("widthchange", this, col, width);
61097         }
61098     },
61099
61100     /**
61101      * Returns the total width of all columns.
61102      * @param {Boolean} includeHidden True to include hidden column widths
61103      * @return {Number}
61104      */
61105     getTotalWidth : function(includeHidden){
61106         if(!this.totalWidth){
61107             this.totalWidth = 0;
61108             for(var i = 0, len = this.config.length; i < len; i++){
61109                 if(includeHidden || !this.isHidden(i)){
61110                     this.totalWidth += this.getColumnWidth(i);
61111                 }
61112             }
61113         }
61114         return this.totalWidth;
61115     },
61116
61117     /**
61118      * Returns the header for the specified column.
61119      * @param {Number} col The column index
61120      * @return {String}
61121      */
61122     getColumnHeader : function(col){
61123         return this.config[col].header;
61124     },
61125
61126     /**
61127      * Sets the header for a column.
61128      * @param {Number} col The column index
61129      * @param {String} header The new header
61130      */
61131     setColumnHeader : function(col, header){
61132         this.config[col].header = header;
61133         this.fireEvent("headerchange", this, col, header);
61134     },
61135
61136     /**
61137      * Returns the tooltip for the specified column.
61138      * @param {Number} col The column index
61139      * @return {String}
61140      */
61141     getColumnTooltip : function(col){
61142             return this.config[col].tooltip;
61143     },
61144     /**
61145      * Sets the tooltip for a column.
61146      * @param {Number} col The column index
61147      * @param {String} tooltip The new tooltip
61148      */
61149     setColumnTooltip : function(col, tooltip){
61150             this.config[col].tooltip = tooltip;
61151     },
61152
61153     /**
61154      * Returns the dataIndex for the specified column.
61155      * @param {Number} col The column index
61156      * @return {Number}
61157      */
61158     getDataIndex : function(col){
61159         return this.config[col].dataIndex;
61160     },
61161
61162     /**
61163      * Sets the dataIndex for a column.
61164      * @param {Number} col The column index
61165      * @param {Number} dataIndex The new dataIndex
61166      */
61167     setDataIndex : function(col, dataIndex){
61168         this.config[col].dataIndex = dataIndex;
61169     },
61170
61171     
61172     
61173     /**
61174      * Returns true if the cell is editable.
61175      * @param {Number} colIndex The column index
61176      * @param {Number} rowIndex The row index - this is nto actually used..?
61177      * @return {Boolean}
61178      */
61179     isCellEditable : function(colIndex, rowIndex){
61180         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61181     },
61182
61183     /**
61184      * Returns the editor defined for the cell/column.
61185      * return false or null to disable editing.
61186      * @param {Number} colIndex The column index
61187      * @param {Number} rowIndex The row index
61188      * @return {Object}
61189      */
61190     getCellEditor : function(colIndex, rowIndex){
61191         return this.config[colIndex].editor;
61192     },
61193
61194     /**
61195      * Sets if a column is editable.
61196      * @param {Number} col The column index
61197      * @param {Boolean} editable True if the column is editable
61198      */
61199     setEditable : function(col, editable){
61200         this.config[col].editable = editable;
61201     },
61202
61203
61204     /**
61205      * Returns true if the column is hidden.
61206      * @param {Number} colIndex The column index
61207      * @return {Boolean}
61208      */
61209     isHidden : function(colIndex){
61210         return this.config[colIndex].hidden;
61211     },
61212
61213
61214     /**
61215      * Returns true if the column width cannot be changed
61216      */
61217     isFixed : function(colIndex){
61218         return this.config[colIndex].fixed;
61219     },
61220
61221     /**
61222      * Returns true if the column can be resized
61223      * @return {Boolean}
61224      */
61225     isResizable : function(colIndex){
61226         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61227     },
61228     /**
61229      * Sets if a column is hidden.
61230      * @param {Number} colIndex The column index
61231      * @param {Boolean} hidden True if the column is hidden
61232      */
61233     setHidden : function(colIndex, hidden){
61234         this.config[colIndex].hidden = hidden;
61235         this.totalWidth = null;
61236         this.fireEvent("hiddenchange", this, colIndex, hidden);
61237     },
61238
61239     /**
61240      * Sets the editor for a column.
61241      * @param {Number} col The column index
61242      * @param {Object} editor The editor object
61243      */
61244     setEditor : function(col, editor){
61245         this.config[col].editor = editor;
61246     },
61247     /**
61248      * Add a column (experimental...) - defaults to adding to the end..
61249      * @param {Object} config 
61250     */
61251     addColumn : function(c)
61252     {
61253     
61254         var i = this.config.length;
61255         this.config[i] = c;
61256         
61257         if(typeof c.dataIndex == "undefined"){
61258             c.dataIndex = i;
61259         }
61260         if(typeof c.renderer == "string"){
61261             c.renderer = Roo.util.Format[c.renderer];
61262         }
61263         if(typeof c.id == "undefined"){
61264             c.id = Roo.id();
61265         }
61266         if(c.editor && c.editor.xtype){
61267             c.editor  = Roo.factory(c.editor, Roo.grid);
61268         }
61269         if(c.editor && c.editor.isFormField){
61270             c.editor = new Roo.grid.GridEditor(c.editor);
61271         }
61272         this.lookup[c.id] = c;
61273     }
61274     
61275 });
61276
61277 Roo.grid.ColumnModel.defaultRenderer = function(value)
61278 {
61279     if(typeof value == "object") {
61280         return value;
61281     }
61282         if(typeof value == "string" && value.length < 1){
61283             return "&#160;";
61284         }
61285     
61286         return String.format("{0}", value);
61287 };
61288
61289 // Alias for backwards compatibility
61290 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61291 /*
61292  * Based on:
61293  * Ext JS Library 1.1.1
61294  * Copyright(c) 2006-2007, Ext JS, LLC.
61295  *
61296  * Originally Released Under LGPL - original licence link has changed is not relivant.
61297  *
61298  * Fork - LGPL
61299  * <script type="text/javascript">
61300  */
61301
61302 /**
61303  * @class Roo.grid.AbstractSelectionModel
61304  * @extends Roo.util.Observable
61305  * @abstract
61306  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61307  * implemented by descendant classes.  This class should not be directly instantiated.
61308  * @constructor
61309  */
61310 Roo.grid.AbstractSelectionModel = function(){
61311     this.locked = false;
61312     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61313 };
61314
61315 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61316     /** @ignore Called by the grid automatically. Do not call directly. */
61317     init : function(grid){
61318         this.grid = grid;
61319         this.initEvents();
61320     },
61321
61322     /**
61323      * Locks the selections.
61324      */
61325     lock : function(){
61326         this.locked = true;
61327     },
61328
61329     /**
61330      * Unlocks the selections.
61331      */
61332     unlock : function(){
61333         this.locked = false;
61334     },
61335
61336     /**
61337      * Returns true if the selections are locked.
61338      * @return {Boolean}
61339      */
61340     isLocked : function(){
61341         return this.locked;
61342     }
61343 });/*
61344  * Based on:
61345  * Ext JS Library 1.1.1
61346  * Copyright(c) 2006-2007, Ext JS, LLC.
61347  *
61348  * Originally Released Under LGPL - original licence link has changed is not relivant.
61349  *
61350  * Fork - LGPL
61351  * <script type="text/javascript">
61352  */
61353 /**
61354  * @extends Roo.grid.AbstractSelectionModel
61355  * @class Roo.grid.RowSelectionModel
61356  * The default SelectionModel used by {@link Roo.grid.Grid}.
61357  * It supports multiple selections and keyboard selection/navigation. 
61358  * @constructor
61359  * @param {Object} config
61360  */
61361 Roo.grid.RowSelectionModel = function(config){
61362     Roo.apply(this, config);
61363     this.selections = new Roo.util.MixedCollection(false, function(o){
61364         return o.id;
61365     });
61366
61367     this.last = false;
61368     this.lastActive = false;
61369
61370     this.addEvents({
61371         /**
61372         * @event selectionchange
61373         * Fires when the selection changes
61374         * @param {SelectionModel} this
61375         */
61376        "selectionchange" : true,
61377        /**
61378         * @event afterselectionchange
61379         * Fires after the selection changes (eg. by key press or clicking)
61380         * @param {SelectionModel} this
61381         */
61382        "afterselectionchange" : true,
61383        /**
61384         * @event beforerowselect
61385         * Fires when a row is selected being selected, return false to cancel.
61386         * @param {SelectionModel} this
61387         * @param {Number} rowIndex The selected index
61388         * @param {Boolean} keepExisting False if other selections will be cleared
61389         */
61390        "beforerowselect" : true,
61391        /**
61392         * @event rowselect
61393         * Fires when a row is selected.
61394         * @param {SelectionModel} this
61395         * @param {Number} rowIndex The selected index
61396         * @param {Roo.data.Record} r The record
61397         */
61398        "rowselect" : true,
61399        /**
61400         * @event rowdeselect
61401         * Fires when a row is deselected.
61402         * @param {SelectionModel} this
61403         * @param {Number} rowIndex The selected index
61404         */
61405         "rowdeselect" : true
61406     });
61407     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61408     this.locked = false;
61409 };
61410
61411 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61412     /**
61413      * @cfg {Boolean} singleSelect
61414      * True to allow selection of only one row at a time (defaults to false)
61415      */
61416     singleSelect : false,
61417
61418     // private
61419     initEvents : function(){
61420
61421         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61422             this.grid.on("mousedown", this.handleMouseDown, this);
61423         }else{ // allow click to work like normal
61424             this.grid.on("rowclick", this.handleDragableRowClick, this);
61425         }
61426         // bootstrap does not have a view..
61427         var view = this.grid.view ? this.grid.view : this.grid;
61428         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61429             "up" : function(e){
61430                 if(!e.shiftKey){
61431                     this.selectPrevious(e.shiftKey);
61432                 }else if(this.last !== false && this.lastActive !== false){
61433                     var last = this.last;
61434                     this.selectRange(this.last,  this.lastActive-1);
61435                     view.focusRow(this.lastActive);
61436                     if(last !== false){
61437                         this.last = last;
61438                     }
61439                 }else{
61440                     this.selectFirstRow();
61441                 }
61442                 this.fireEvent("afterselectionchange", this);
61443             },
61444             "down" : function(e){
61445                 if(!e.shiftKey){
61446                     this.selectNext(e.shiftKey);
61447                 }else if(this.last !== false && this.lastActive !== false){
61448                     var last = this.last;
61449                     this.selectRange(this.last,  this.lastActive+1);
61450                     view.focusRow(this.lastActive);
61451                     if(last !== false){
61452                         this.last = last;
61453                     }
61454                 }else{
61455                     this.selectFirstRow();
61456                 }
61457                 this.fireEvent("afterselectionchange", this);
61458             },
61459             scope: this
61460         });
61461
61462          
61463         view.on("refresh", this.onRefresh, this);
61464         view.on("rowupdated", this.onRowUpdated, this);
61465         view.on("rowremoved", this.onRemove, this);
61466     },
61467
61468     // private
61469     onRefresh : function(){
61470         var ds = this.grid.ds, i, v = this.grid.view;
61471         var s = this.selections;
61472         s.each(function(r){
61473             if((i = ds.indexOfId(r.id)) != -1){
61474                 v.onRowSelect(i);
61475                 s.add(ds.getAt(i)); // updating the selection relate data
61476             }else{
61477                 s.remove(r);
61478             }
61479         });
61480     },
61481
61482     // private
61483     onRemove : function(v, index, r){
61484         this.selections.remove(r);
61485     },
61486
61487     // private
61488     onRowUpdated : function(v, index, r){
61489         if(this.isSelected(r)){
61490             v.onRowSelect(index);
61491         }
61492     },
61493
61494     /**
61495      * Select records.
61496      * @param {Array} records The records to select
61497      * @param {Boolean} keepExisting (optional) True to keep existing selections
61498      */
61499     selectRecords : function(records, keepExisting){
61500         if(!keepExisting){
61501             this.clearSelections();
61502         }
61503         var ds = this.grid.ds;
61504         for(var i = 0, len = records.length; i < len; i++){
61505             this.selectRow(ds.indexOf(records[i]), true);
61506         }
61507     },
61508
61509     /**
61510      * Gets the number of selected rows.
61511      * @return {Number}
61512      */
61513     getCount : function(){
61514         return this.selections.length;
61515     },
61516
61517     /**
61518      * Selects the first row in the grid.
61519      */
61520     selectFirstRow : function(){
61521         this.selectRow(0);
61522     },
61523
61524     /**
61525      * Select the last row.
61526      * @param {Boolean} keepExisting (optional) True to keep existing selections
61527      */
61528     selectLastRow : function(keepExisting){
61529         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61530     },
61531
61532     /**
61533      * Selects the row immediately following the last selected row.
61534      * @param {Boolean} keepExisting (optional) True to keep existing selections
61535      */
61536     selectNext : function(keepExisting){
61537         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61538             this.selectRow(this.last+1, keepExisting);
61539             var view = this.grid.view ? this.grid.view : this.grid;
61540             view.focusRow(this.last);
61541         }
61542     },
61543
61544     /**
61545      * Selects the row that precedes the last selected row.
61546      * @param {Boolean} keepExisting (optional) True to keep existing selections
61547      */
61548     selectPrevious : function(keepExisting){
61549         if(this.last){
61550             this.selectRow(this.last-1, keepExisting);
61551             var view = this.grid.view ? this.grid.view : this.grid;
61552             view.focusRow(this.last);
61553         }
61554     },
61555
61556     /**
61557      * Returns the selected records
61558      * @return {Array} Array of selected records
61559      */
61560     getSelections : function(){
61561         return [].concat(this.selections.items);
61562     },
61563
61564     /**
61565      * Returns the first selected record.
61566      * @return {Record}
61567      */
61568     getSelected : function(){
61569         return this.selections.itemAt(0);
61570     },
61571
61572
61573     /**
61574      * Clears all selections.
61575      */
61576     clearSelections : function(fast){
61577         if(this.locked) {
61578             return;
61579         }
61580         if(fast !== true){
61581             var ds = this.grid.ds;
61582             var s = this.selections;
61583             s.each(function(r){
61584                 this.deselectRow(ds.indexOfId(r.id));
61585             }, this);
61586             s.clear();
61587         }else{
61588             this.selections.clear();
61589         }
61590         this.last = false;
61591     },
61592
61593
61594     /**
61595      * Selects all rows.
61596      */
61597     selectAll : function(){
61598         if(this.locked) {
61599             return;
61600         }
61601         this.selections.clear();
61602         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61603             this.selectRow(i, true);
61604         }
61605     },
61606
61607     /**
61608      * Returns True if there is a selection.
61609      * @return {Boolean}
61610      */
61611     hasSelection : function(){
61612         return this.selections.length > 0;
61613     },
61614
61615     /**
61616      * Returns True if the specified row is selected.
61617      * @param {Number/Record} record The record or index of the record to check
61618      * @return {Boolean}
61619      */
61620     isSelected : function(index){
61621         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61622         return (r && this.selections.key(r.id) ? true : false);
61623     },
61624
61625     /**
61626      * Returns True if the specified record id is selected.
61627      * @param {String} id The id of record to check
61628      * @return {Boolean}
61629      */
61630     isIdSelected : function(id){
61631         return (this.selections.key(id) ? true : false);
61632     },
61633
61634     // private
61635     handleMouseDown : function(e, t)
61636     {
61637         var view = this.grid.view ? this.grid.view : this.grid;
61638         var rowIndex;
61639         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61640             return;
61641         };
61642         if(e.shiftKey && this.last !== false){
61643             var last = this.last;
61644             this.selectRange(last, rowIndex, e.ctrlKey);
61645             this.last = last; // reset the last
61646             view.focusRow(rowIndex);
61647         }else{
61648             var isSelected = this.isSelected(rowIndex);
61649             if(e.button !== 0 && isSelected){
61650                 view.focusRow(rowIndex);
61651             }else if(e.ctrlKey && isSelected){
61652                 this.deselectRow(rowIndex);
61653             }else if(!isSelected){
61654                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61655                 view.focusRow(rowIndex);
61656             }
61657         }
61658         this.fireEvent("afterselectionchange", this);
61659     },
61660     // private
61661     handleDragableRowClick :  function(grid, rowIndex, e) 
61662     {
61663         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61664             this.selectRow(rowIndex, false);
61665             var view = this.grid.view ? this.grid.view : this.grid;
61666             view.focusRow(rowIndex);
61667              this.fireEvent("afterselectionchange", this);
61668         }
61669     },
61670     
61671     /**
61672      * Selects multiple rows.
61673      * @param {Array} rows Array of the indexes of the row to select
61674      * @param {Boolean} keepExisting (optional) True to keep existing selections
61675      */
61676     selectRows : function(rows, keepExisting){
61677         if(!keepExisting){
61678             this.clearSelections();
61679         }
61680         for(var i = 0, len = rows.length; i < len; i++){
61681             this.selectRow(rows[i], true);
61682         }
61683     },
61684
61685     /**
61686      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61687      * @param {Number} startRow The index of the first row in the range
61688      * @param {Number} endRow The index of the last row in the range
61689      * @param {Boolean} keepExisting (optional) True to retain existing selections
61690      */
61691     selectRange : function(startRow, endRow, keepExisting){
61692         if(this.locked) {
61693             return;
61694         }
61695         if(!keepExisting){
61696             this.clearSelections();
61697         }
61698         if(startRow <= endRow){
61699             for(var i = startRow; i <= endRow; i++){
61700                 this.selectRow(i, true);
61701             }
61702         }else{
61703             for(var i = startRow; i >= endRow; i--){
61704                 this.selectRow(i, true);
61705             }
61706         }
61707     },
61708
61709     /**
61710      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61711      * @param {Number} startRow The index of the first row in the range
61712      * @param {Number} endRow The index of the last row in the range
61713      */
61714     deselectRange : function(startRow, endRow, preventViewNotify){
61715         if(this.locked) {
61716             return;
61717         }
61718         for(var i = startRow; i <= endRow; i++){
61719             this.deselectRow(i, preventViewNotify);
61720         }
61721     },
61722
61723     /**
61724      * Selects a row.
61725      * @param {Number} row The index of the row to select
61726      * @param {Boolean} keepExisting (optional) True to keep existing selections
61727      */
61728     selectRow : function(index, keepExisting, preventViewNotify){
61729         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61730             return;
61731         }
61732         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61733             if(!keepExisting || this.singleSelect){
61734                 this.clearSelections();
61735             }
61736             var r = this.grid.ds.getAt(index);
61737             this.selections.add(r);
61738             this.last = this.lastActive = index;
61739             if(!preventViewNotify){
61740                 var view = this.grid.view ? this.grid.view : this.grid;
61741                 view.onRowSelect(index);
61742             }
61743             this.fireEvent("rowselect", this, index, r);
61744             this.fireEvent("selectionchange", this);
61745         }
61746     },
61747
61748     /**
61749      * Deselects a row.
61750      * @param {Number} row The index of the row to deselect
61751      */
61752     deselectRow : function(index, preventViewNotify){
61753         if(this.locked) {
61754             return;
61755         }
61756         if(this.last == index){
61757             this.last = false;
61758         }
61759         if(this.lastActive == index){
61760             this.lastActive = false;
61761         }
61762         var r = this.grid.ds.getAt(index);
61763         this.selections.remove(r);
61764         if(!preventViewNotify){
61765             var view = this.grid.view ? this.grid.view : this.grid;
61766             view.onRowDeselect(index);
61767         }
61768         this.fireEvent("rowdeselect", this, index);
61769         this.fireEvent("selectionchange", this);
61770     },
61771
61772     // private
61773     restoreLast : function(){
61774         if(this._last){
61775             this.last = this._last;
61776         }
61777     },
61778
61779     // private
61780     acceptsNav : function(row, col, cm){
61781         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61782     },
61783
61784     // private
61785     onEditorKey : function(field, e){
61786         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61787         if(k == e.TAB){
61788             e.stopEvent();
61789             ed.completeEdit();
61790             if(e.shiftKey){
61791                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61792             }else{
61793                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61794             }
61795         }else if(k == e.ENTER && !e.ctrlKey){
61796             e.stopEvent();
61797             ed.completeEdit();
61798             if(e.shiftKey){
61799                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61800             }else{
61801                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61802             }
61803         }else if(k == e.ESC){
61804             ed.cancelEdit();
61805         }
61806         if(newCell){
61807             g.startEditing(newCell[0], newCell[1]);
61808         }
61809     }
61810 });/*
61811  * Based on:
61812  * Ext JS Library 1.1.1
61813  * Copyright(c) 2006-2007, Ext JS, LLC.
61814  *
61815  * Originally Released Under LGPL - original licence link has changed is not relivant.
61816  *
61817  * Fork - LGPL
61818  * <script type="text/javascript">
61819  */
61820 /**
61821  * @class Roo.grid.CellSelectionModel
61822  * @extends Roo.grid.AbstractSelectionModel
61823  * This class provides the basic implementation for cell selection in a grid.
61824  * @constructor
61825  * @param {Object} config The object containing the configuration of this model.
61826  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61827  */
61828 Roo.grid.CellSelectionModel = function(config){
61829     Roo.apply(this, config);
61830
61831     this.selection = null;
61832
61833     this.addEvents({
61834         /**
61835              * @event beforerowselect
61836              * Fires before a cell is selected.
61837              * @param {SelectionModel} this
61838              * @param {Number} rowIndex The selected row index
61839              * @param {Number} colIndex The selected cell index
61840              */
61841             "beforecellselect" : true,
61842         /**
61843              * @event cellselect
61844              * Fires when a cell is selected.
61845              * @param {SelectionModel} this
61846              * @param {Number} rowIndex The selected row index
61847              * @param {Number} colIndex The selected cell index
61848              */
61849             "cellselect" : true,
61850         /**
61851              * @event selectionchange
61852              * Fires when the active selection changes.
61853              * @param {SelectionModel} this
61854              * @param {Object} selection null for no selection or an object (o) with two properties
61855                 <ul>
61856                 <li>o.record: the record object for the row the selection is in</li>
61857                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61858                 </ul>
61859              */
61860             "selectionchange" : true,
61861         /**
61862              * @event tabend
61863              * Fires when the tab (or enter) was pressed on the last editable cell
61864              * You can use this to trigger add new row.
61865              * @param {SelectionModel} this
61866              */
61867             "tabend" : true,
61868          /**
61869              * @event beforeeditnext
61870              * Fires before the next editable sell is made active
61871              * You can use this to skip to another cell or fire the tabend
61872              *    if you set cell to false
61873              * @param {Object} eventdata object : { cell : [ row, col ] } 
61874              */
61875             "beforeeditnext" : true
61876     });
61877     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61878 };
61879
61880 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61881     
61882     enter_is_tab: false,
61883
61884     /** @ignore */
61885     initEvents : function(){
61886         this.grid.on("mousedown", this.handleMouseDown, this);
61887         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61888         var view = this.grid.view;
61889         view.on("refresh", this.onViewChange, this);
61890         view.on("rowupdated", this.onRowUpdated, this);
61891         view.on("beforerowremoved", this.clearSelections, this);
61892         view.on("beforerowsinserted", this.clearSelections, this);
61893         if(this.grid.isEditor){
61894             this.grid.on("beforeedit", this.beforeEdit,  this);
61895         }
61896     },
61897
61898         //private
61899     beforeEdit : function(e){
61900         this.select(e.row, e.column, false, true, e.record);
61901     },
61902
61903         //private
61904     onRowUpdated : function(v, index, r){
61905         if(this.selection && this.selection.record == r){
61906             v.onCellSelect(index, this.selection.cell[1]);
61907         }
61908     },
61909
61910         //private
61911     onViewChange : function(){
61912         this.clearSelections(true);
61913     },
61914
61915         /**
61916          * Returns the currently selected cell,.
61917          * @return {Array} The selected cell (row, column) or null if none selected.
61918          */
61919     getSelectedCell : function(){
61920         return this.selection ? this.selection.cell : null;
61921     },
61922
61923     /**
61924      * Clears all selections.
61925      * @param {Boolean} true to prevent the gridview from being notified about the change.
61926      */
61927     clearSelections : function(preventNotify){
61928         var s = this.selection;
61929         if(s){
61930             if(preventNotify !== true){
61931                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61932             }
61933             this.selection = null;
61934             this.fireEvent("selectionchange", this, null);
61935         }
61936     },
61937
61938     /**
61939      * Returns true if there is a selection.
61940      * @return {Boolean}
61941      */
61942     hasSelection : function(){
61943         return this.selection ? true : false;
61944     },
61945
61946     /** @ignore */
61947     handleMouseDown : function(e, t){
61948         var v = this.grid.getView();
61949         if(this.isLocked()){
61950             return;
61951         };
61952         var row = v.findRowIndex(t);
61953         var cell = v.findCellIndex(t);
61954         if(row !== false && cell !== false){
61955             this.select(row, cell);
61956         }
61957     },
61958
61959     /**
61960      * Selects a cell.
61961      * @param {Number} rowIndex
61962      * @param {Number} collIndex
61963      */
61964     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61965         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61966             this.clearSelections();
61967             r = r || this.grid.dataSource.getAt(rowIndex);
61968             this.selection = {
61969                 record : r,
61970                 cell : [rowIndex, colIndex]
61971             };
61972             if(!preventViewNotify){
61973                 var v = this.grid.getView();
61974                 v.onCellSelect(rowIndex, colIndex);
61975                 if(preventFocus !== true){
61976                     v.focusCell(rowIndex, colIndex);
61977                 }
61978             }
61979             this.fireEvent("cellselect", this, rowIndex, colIndex);
61980             this.fireEvent("selectionchange", this, this.selection);
61981         }
61982     },
61983
61984         //private
61985     isSelectable : function(rowIndex, colIndex, cm){
61986         return !cm.isHidden(colIndex);
61987     },
61988
61989     /** @ignore */
61990     handleKeyDown : function(e){
61991         //Roo.log('Cell Sel Model handleKeyDown');
61992         if(!e.isNavKeyPress()){
61993             return;
61994         }
61995         var g = this.grid, s = this.selection;
61996         if(!s){
61997             e.stopEvent();
61998             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
61999             if(cell){
62000                 this.select(cell[0], cell[1]);
62001             }
62002             return;
62003         }
62004         var sm = this;
62005         var walk = function(row, col, step){
62006             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62007         };
62008         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62009         var newCell;
62010
62011       
62012
62013         switch(k){
62014             case e.TAB:
62015                 // handled by onEditorKey
62016                 if (g.isEditor && g.editing) {
62017                     return;
62018                 }
62019                 if(e.shiftKey) {
62020                     newCell = walk(r, c-1, -1);
62021                 } else {
62022                     newCell = walk(r, c+1, 1);
62023                 }
62024                 break;
62025             
62026             case e.DOWN:
62027                newCell = walk(r+1, c, 1);
62028                 break;
62029             
62030             case e.UP:
62031                 newCell = walk(r-1, c, -1);
62032                 break;
62033             
62034             case e.RIGHT:
62035                 newCell = walk(r, c+1, 1);
62036                 break;
62037             
62038             case e.LEFT:
62039                 newCell = walk(r, c-1, -1);
62040                 break;
62041             
62042             case e.ENTER:
62043                 
62044                 if(g.isEditor && !g.editing){
62045                    g.startEditing(r, c);
62046                    e.stopEvent();
62047                    return;
62048                 }
62049                 
62050                 
62051              break;
62052         };
62053         if(newCell){
62054             this.select(newCell[0], newCell[1]);
62055             e.stopEvent();
62056             
62057         }
62058     },
62059
62060     acceptsNav : function(row, col, cm){
62061         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62062     },
62063     /**
62064      * Selects a cell.
62065      * @param {Number} field (not used) - as it's normally used as a listener
62066      * @param {Number} e - event - fake it by using
62067      *
62068      * var e = Roo.EventObjectImpl.prototype;
62069      * e.keyCode = e.TAB
62070      *
62071      * 
62072      */
62073     onEditorKey : function(field, e){
62074         
62075         var k = e.getKey(),
62076             newCell,
62077             g = this.grid,
62078             ed = g.activeEditor,
62079             forward = false;
62080         ///Roo.log('onEditorKey' + k);
62081         
62082         
62083         if (this.enter_is_tab && k == e.ENTER) {
62084             k = e.TAB;
62085         }
62086         
62087         if(k == e.TAB){
62088             if(e.shiftKey){
62089                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62090             }else{
62091                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62092                 forward = true;
62093             }
62094             
62095             e.stopEvent();
62096             
62097         } else if(k == e.ENTER &&  !e.ctrlKey){
62098             ed.completeEdit();
62099             e.stopEvent();
62100             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62101         
62102                 } else if(k == e.ESC){
62103             ed.cancelEdit();
62104         }
62105                 
62106         if (newCell) {
62107             var ecall = { cell : newCell, forward : forward };
62108             this.fireEvent('beforeeditnext', ecall );
62109             newCell = ecall.cell;
62110                         forward = ecall.forward;
62111         }
62112                 
62113         if(newCell){
62114             //Roo.log('next cell after edit');
62115             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62116         } else if (forward) {
62117             // tabbed past last
62118             this.fireEvent.defer(100, this, ['tabend',this]);
62119         }
62120     }
62121 });/*
62122  * Based on:
62123  * Ext JS Library 1.1.1
62124  * Copyright(c) 2006-2007, Ext JS, LLC.
62125  *
62126  * Originally Released Under LGPL - original licence link has changed is not relivant.
62127  *
62128  * Fork - LGPL
62129  * <script type="text/javascript">
62130  */
62131  
62132 /**
62133  * @class Roo.grid.EditorGrid
62134  * @extends Roo.grid.Grid
62135  * Class for creating and editable grid.
62136  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62137  * The container MUST have some type of size defined for the grid to fill. The container will be 
62138  * automatically set to position relative if it isn't already.
62139  * @param {Object} dataSource The data model to bind to
62140  * @param {Object} colModel The column model with info about this grid's columns
62141  */
62142 Roo.grid.EditorGrid = function(container, config){
62143     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62144     this.getGridEl().addClass("xedit-grid");
62145
62146     if(!this.selModel){
62147         this.selModel = new Roo.grid.CellSelectionModel();
62148     }
62149
62150     this.activeEditor = null;
62151
62152         this.addEvents({
62153             /**
62154              * @event beforeedit
62155              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62156              * <ul style="padding:5px;padding-left:16px;">
62157              * <li>grid - This grid</li>
62158              * <li>record - The record being edited</li>
62159              * <li>field - The field name being edited</li>
62160              * <li>value - The value for the field being edited.</li>
62161              * <li>row - The grid row index</li>
62162              * <li>column - The grid column index</li>
62163              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62164              * </ul>
62165              * @param {Object} e An edit event (see above for description)
62166              */
62167             "beforeedit" : true,
62168             /**
62169              * @event afteredit
62170              * Fires after a cell is edited. <br />
62171              * <ul style="padding:5px;padding-left:16px;">
62172              * <li>grid - This grid</li>
62173              * <li>record - The record being edited</li>
62174              * <li>field - The field name being edited</li>
62175              * <li>value - The value being set</li>
62176              * <li>originalValue - The original value for the field, before the edit.</li>
62177              * <li>row - The grid row index</li>
62178              * <li>column - The grid column index</li>
62179              * </ul>
62180              * @param {Object} e An edit event (see above for description)
62181              */
62182             "afteredit" : true,
62183             /**
62184              * @event validateedit
62185              * Fires after a cell is edited, but before the value is set in the record. 
62186          * You can use this to modify the value being set in the field, Return false
62187              * to cancel the change. The edit event object has the following properties <br />
62188              * <ul style="padding:5px;padding-left:16px;">
62189          * <li>editor - This editor</li>
62190              * <li>grid - This grid</li>
62191              * <li>record - The record being edited</li>
62192              * <li>field - The field name being edited</li>
62193              * <li>value - The value being set</li>
62194              * <li>originalValue - The original value for the field, before the edit.</li>
62195              * <li>row - The grid row index</li>
62196              * <li>column - The grid column index</li>
62197              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62198              * </ul>
62199              * @param {Object} e An edit event (see above for description)
62200              */
62201             "validateedit" : true
62202         });
62203     this.on("bodyscroll", this.stopEditing,  this);
62204     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62205 };
62206
62207 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62208     /**
62209      * @cfg {Number} clicksToEdit
62210      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62211      */
62212     clicksToEdit: 2,
62213
62214     // private
62215     isEditor : true,
62216     // private
62217     trackMouseOver: false, // causes very odd FF errors
62218
62219     onCellDblClick : function(g, row, col){
62220         this.startEditing(row, col);
62221     },
62222
62223     onEditComplete : function(ed, value, startValue){
62224         this.editing = false;
62225         this.activeEditor = null;
62226         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62227         var r = ed.record;
62228         var field = this.colModel.getDataIndex(ed.col);
62229         var e = {
62230             grid: this,
62231             record: r,
62232             field: field,
62233             originalValue: startValue,
62234             value: value,
62235             row: ed.row,
62236             column: ed.col,
62237             cancel:false,
62238             editor: ed
62239         };
62240         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62241         cell.show();
62242           
62243         if(String(value) !== String(startValue)){
62244             
62245             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62246                 r.set(field, e.value);
62247                 // if we are dealing with a combo box..
62248                 // then we also set the 'name' colum to be the displayField
62249                 if (ed.field.displayField && ed.field.name) {
62250                     r.set(ed.field.name, ed.field.el.dom.value);
62251                 }
62252                 
62253                 delete e.cancel; //?? why!!!
62254                 this.fireEvent("afteredit", e);
62255             }
62256         } else {
62257             this.fireEvent("afteredit", e); // always fire it!
62258         }
62259         this.view.focusCell(ed.row, ed.col);
62260     },
62261
62262     /**
62263      * Starts editing the specified for the specified row/column
62264      * @param {Number} rowIndex
62265      * @param {Number} colIndex
62266      */
62267     startEditing : function(row, col){
62268         this.stopEditing();
62269         if(this.colModel.isCellEditable(col, row)){
62270             this.view.ensureVisible(row, col, true);
62271           
62272             var r = this.dataSource.getAt(row);
62273             var field = this.colModel.getDataIndex(col);
62274             var cell = Roo.get(this.view.getCell(row,col));
62275             var e = {
62276                 grid: this,
62277                 record: r,
62278                 field: field,
62279                 value: r.data[field],
62280                 row: row,
62281                 column: col,
62282                 cancel:false 
62283             };
62284             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62285                 this.editing = true;
62286                 var ed = this.colModel.getCellEditor(col, row);
62287                 
62288                 if (!ed) {
62289                     return;
62290                 }
62291                 if(!ed.rendered){
62292                     ed.render(ed.parentEl || document.body);
62293                 }
62294                 ed.field.reset();
62295                
62296                 cell.hide();
62297                 
62298                 (function(){ // complex but required for focus issues in safari, ie and opera
62299                     ed.row = row;
62300                     ed.col = col;
62301                     ed.record = r;
62302                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62303                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62304                     this.activeEditor = ed;
62305                     var v = r.data[field];
62306                     ed.startEdit(this.view.getCell(row, col), v);
62307                     // combo's with 'displayField and name set
62308                     if (ed.field.displayField && ed.field.name) {
62309                         ed.field.el.dom.value = r.data[ed.field.name];
62310                     }
62311                     
62312                     
62313                 }).defer(50, this);
62314             }
62315         }
62316     },
62317         
62318     /**
62319      * Stops any active editing
62320      */
62321     stopEditing : function(){
62322         if(this.activeEditor){
62323             this.activeEditor.completeEdit();
62324         }
62325         this.activeEditor = null;
62326     },
62327         
62328          /**
62329      * Called to get grid's drag proxy text, by default returns this.ddText.
62330      * @return {String}
62331      */
62332     getDragDropText : function(){
62333         var count = this.selModel.getSelectedCell() ? 1 : 0;
62334         return String.format(this.ddText, count, count == 1 ? '' : 's');
62335     }
62336         
62337 });/*
62338  * Based on:
62339  * Ext JS Library 1.1.1
62340  * Copyright(c) 2006-2007, Ext JS, LLC.
62341  *
62342  * Originally Released Under LGPL - original licence link has changed is not relivant.
62343  *
62344  * Fork - LGPL
62345  * <script type="text/javascript">
62346  */
62347
62348 // private - not really -- you end up using it !
62349 // This is a support class used internally by the Grid components
62350
62351 /**
62352  * @class Roo.grid.GridEditor
62353  * @extends Roo.Editor
62354  * Class for creating and editable grid elements.
62355  * @param {Object} config any settings (must include field)
62356  */
62357 Roo.grid.GridEditor = function(field, config){
62358     if (!config && field.field) {
62359         config = field;
62360         field = Roo.factory(config.field, Roo.form);
62361     }
62362     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62363     field.monitorTab = false;
62364 };
62365
62366 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62367     
62368     /**
62369      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62370      */
62371     
62372     alignment: "tl-tl",
62373     autoSize: "width",
62374     hideEl : false,
62375     cls: "x-small-editor x-grid-editor",
62376     shim:false,
62377     shadow:"frame"
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
62390   
62391 Roo.grid.PropertyRecord = Roo.data.Record.create([
62392     {name:'name',type:'string'},  'value'
62393 ]);
62394
62395
62396 Roo.grid.PropertyStore = function(grid, source){
62397     this.grid = grid;
62398     this.store = new Roo.data.Store({
62399         recordType : Roo.grid.PropertyRecord
62400     });
62401     this.store.on('update', this.onUpdate,  this);
62402     if(source){
62403         this.setSource(source);
62404     }
62405     Roo.grid.PropertyStore.superclass.constructor.call(this);
62406 };
62407
62408
62409
62410 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62411     setSource : function(o){
62412         this.source = o;
62413         this.store.removeAll();
62414         var data = [];
62415         for(var k in o){
62416             if(this.isEditableValue(o[k])){
62417                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62418             }
62419         }
62420         this.store.loadRecords({records: data}, {}, true);
62421     },
62422
62423     onUpdate : function(ds, record, type){
62424         if(type == Roo.data.Record.EDIT){
62425             var v = record.data['value'];
62426             var oldValue = record.modified['value'];
62427             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62428                 this.source[record.id] = v;
62429                 record.commit();
62430                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62431             }else{
62432                 record.reject();
62433             }
62434         }
62435     },
62436
62437     getProperty : function(row){
62438        return this.store.getAt(row);
62439     },
62440
62441     isEditableValue: function(val){
62442         if(val && val instanceof Date){
62443             return true;
62444         }else if(typeof val == 'object' || typeof val == 'function'){
62445             return false;
62446         }
62447         return true;
62448     },
62449
62450     setValue : function(prop, value){
62451         this.source[prop] = value;
62452         this.store.getById(prop).set('value', value);
62453     },
62454
62455     getSource : function(){
62456         return this.source;
62457     }
62458 });
62459
62460 Roo.grid.PropertyColumnModel = function(grid, store){
62461     this.grid = grid;
62462     var g = Roo.grid;
62463     g.PropertyColumnModel.superclass.constructor.call(this, [
62464         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62465         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62466     ]);
62467     this.store = store;
62468     this.bselect = Roo.DomHelper.append(document.body, {
62469         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62470             {tag: 'option', value: 'true', html: 'true'},
62471             {tag: 'option', value: 'false', html: 'false'}
62472         ]
62473     });
62474     Roo.id(this.bselect);
62475     var f = Roo.form;
62476     this.editors = {
62477         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62478         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62479         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62480         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62481         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62482     };
62483     this.renderCellDelegate = this.renderCell.createDelegate(this);
62484     this.renderPropDelegate = this.renderProp.createDelegate(this);
62485 };
62486
62487 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62488     
62489     
62490     nameText : 'Name',
62491     valueText : 'Value',
62492     
62493     dateFormat : 'm/j/Y',
62494     
62495     
62496     renderDate : function(dateVal){
62497         return dateVal.dateFormat(this.dateFormat);
62498     },
62499
62500     renderBool : function(bVal){
62501         return bVal ? 'true' : 'false';
62502     },
62503
62504     isCellEditable : function(colIndex, rowIndex){
62505         return colIndex == 1;
62506     },
62507
62508     getRenderer : function(col){
62509         return col == 1 ?
62510             this.renderCellDelegate : this.renderPropDelegate;
62511     },
62512
62513     renderProp : function(v){
62514         return this.getPropertyName(v);
62515     },
62516
62517     renderCell : function(val){
62518         var rv = val;
62519         if(val instanceof Date){
62520             rv = this.renderDate(val);
62521         }else if(typeof val == 'boolean'){
62522             rv = this.renderBool(val);
62523         }
62524         return Roo.util.Format.htmlEncode(rv);
62525     },
62526
62527     getPropertyName : function(name){
62528         var pn = this.grid.propertyNames;
62529         return pn && pn[name] ? pn[name] : name;
62530     },
62531
62532     getCellEditor : function(colIndex, rowIndex){
62533         var p = this.store.getProperty(rowIndex);
62534         var n = p.data['name'], val = p.data['value'];
62535         
62536         if(typeof(this.grid.customEditors[n]) == 'string'){
62537             return this.editors[this.grid.customEditors[n]];
62538         }
62539         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62540             return this.grid.customEditors[n];
62541         }
62542         if(val instanceof Date){
62543             return this.editors['date'];
62544         }else if(typeof val == 'number'){
62545             return this.editors['number'];
62546         }else if(typeof val == 'boolean'){
62547             return this.editors['boolean'];
62548         }else{
62549             return this.editors['string'];
62550         }
62551     }
62552 });
62553
62554 /**
62555  * @class Roo.grid.PropertyGrid
62556  * @extends Roo.grid.EditorGrid
62557  * This class represents the  interface of a component based property grid control.
62558  * <br><br>Usage:<pre><code>
62559  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62560       
62561  });
62562  // set any options
62563  grid.render();
62564  * </code></pre>
62565   
62566  * @constructor
62567  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62568  * The container MUST have some type of size defined for the grid to fill. The container will be
62569  * automatically set to position relative if it isn't already.
62570  * @param {Object} config A config object that sets properties on this grid.
62571  */
62572 Roo.grid.PropertyGrid = function(container, config){
62573     config = config || {};
62574     var store = new Roo.grid.PropertyStore(this);
62575     this.store = store;
62576     var cm = new Roo.grid.PropertyColumnModel(this, store);
62577     store.store.sort('name', 'ASC');
62578     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62579         ds: store.store,
62580         cm: cm,
62581         enableColLock:false,
62582         enableColumnMove:false,
62583         stripeRows:false,
62584         trackMouseOver: false,
62585         clicksToEdit:1
62586     }, config));
62587     this.getGridEl().addClass('x-props-grid');
62588     this.lastEditRow = null;
62589     this.on('columnresize', this.onColumnResize, this);
62590     this.addEvents({
62591          /**
62592              * @event beforepropertychange
62593              * Fires before a property changes (return false to stop?)
62594              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62595              * @param {String} id Record Id
62596              * @param {String} newval New Value
62597          * @param {String} oldval Old Value
62598              */
62599         "beforepropertychange": true,
62600         /**
62601              * @event propertychange
62602              * Fires after a property changes
62603              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62604              * @param {String} id Record Id
62605              * @param {String} newval New Value
62606          * @param {String} oldval Old Value
62607              */
62608         "propertychange": true
62609     });
62610     this.customEditors = this.customEditors || {};
62611 };
62612 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62613     
62614      /**
62615      * @cfg {Object} customEditors map of colnames=> custom editors.
62616      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62617      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62618      * false disables editing of the field.
62619          */
62620     
62621       /**
62622      * @cfg {Object} propertyNames map of property Names to their displayed value
62623          */
62624     
62625     render : function(){
62626         Roo.grid.PropertyGrid.superclass.render.call(this);
62627         this.autoSize.defer(100, this);
62628     },
62629
62630     autoSize : function(){
62631         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62632         if(this.view){
62633             this.view.fitColumns();
62634         }
62635     },
62636
62637     onColumnResize : function(){
62638         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62639         this.autoSize();
62640     },
62641     /**
62642      * Sets the data for the Grid
62643      * accepts a Key => Value object of all the elements avaiable.
62644      * @param {Object} data  to appear in grid.
62645      */
62646     setSource : function(source){
62647         this.store.setSource(source);
62648         //this.autoSize();
62649     },
62650     /**
62651      * Gets all the data from the grid.
62652      * @return {Object} data  data stored in grid
62653      */
62654     getSource : function(){
62655         return this.store.getSource();
62656     }
62657 });/*
62658   
62659  * Licence LGPL
62660  
62661  */
62662  
62663 /**
62664  * @class Roo.grid.Calendar
62665  * @extends Roo.grid.Grid
62666  * This class extends the Grid to provide a calendar widget
62667  * <br><br>Usage:<pre><code>
62668  var grid = new Roo.grid.Calendar("my-container-id", {
62669      ds: myDataStore,
62670      cm: myColModel,
62671      selModel: mySelectionModel,
62672      autoSizeColumns: true,
62673      monitorWindowResize: false,
62674      trackMouseOver: true
62675      eventstore : real data store..
62676  });
62677  // set any options
62678  grid.render();
62679   
62680   * @constructor
62681  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62682  * The container MUST have some type of size defined for the grid to fill. The container will be
62683  * automatically set to position relative if it isn't already.
62684  * @param {Object} config A config object that sets properties on this grid.
62685  */
62686 Roo.grid.Calendar = function(container, config){
62687         // initialize the container
62688         this.container = Roo.get(container);
62689         this.container.update("");
62690         this.container.setStyle("overflow", "hidden");
62691     this.container.addClass('x-grid-container');
62692
62693     this.id = this.container.id;
62694
62695     Roo.apply(this, config);
62696     // check and correct shorthanded configs
62697     
62698     var rows = [];
62699     var d =1;
62700     for (var r = 0;r < 6;r++) {
62701         
62702         rows[r]=[];
62703         for (var c =0;c < 7;c++) {
62704             rows[r][c]= '';
62705         }
62706     }
62707     if (this.eventStore) {
62708         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62709         this.eventStore.on('load',this.onLoad, this);
62710         this.eventStore.on('beforeload',this.clearEvents, this);
62711          
62712     }
62713     
62714     this.dataSource = new Roo.data.Store({
62715             proxy: new Roo.data.MemoryProxy(rows),
62716             reader: new Roo.data.ArrayReader({}, [
62717                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62718     });
62719
62720     this.dataSource.load();
62721     this.ds = this.dataSource;
62722     this.ds.xmodule = this.xmodule || false;
62723     
62724     
62725     var cellRender = function(v,x,r)
62726     {
62727         return String.format(
62728             '<div class="fc-day  fc-widget-content"><div>' +
62729                 '<div class="fc-event-container"></div>' +
62730                 '<div class="fc-day-number">{0}</div>'+
62731                 
62732                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62733             '</div></div>', v);
62734     
62735     }
62736     
62737     
62738     this.colModel = new Roo.grid.ColumnModel( [
62739         {
62740             xtype: 'ColumnModel',
62741             xns: Roo.grid,
62742             dataIndex : 'weekday0',
62743             header : 'Sunday',
62744             renderer : cellRender
62745         },
62746         {
62747             xtype: 'ColumnModel',
62748             xns: Roo.grid,
62749             dataIndex : 'weekday1',
62750             header : 'Monday',
62751             renderer : cellRender
62752         },
62753         {
62754             xtype: 'ColumnModel',
62755             xns: Roo.grid,
62756             dataIndex : 'weekday2',
62757             header : 'Tuesday',
62758             renderer : cellRender
62759         },
62760         {
62761             xtype: 'ColumnModel',
62762             xns: Roo.grid,
62763             dataIndex : 'weekday3',
62764             header : 'Wednesday',
62765             renderer : cellRender
62766         },
62767         {
62768             xtype: 'ColumnModel',
62769             xns: Roo.grid,
62770             dataIndex : 'weekday4',
62771             header : 'Thursday',
62772             renderer : cellRender
62773         },
62774         {
62775             xtype: 'ColumnModel',
62776             xns: Roo.grid,
62777             dataIndex : 'weekday5',
62778             header : 'Friday',
62779             renderer : cellRender
62780         },
62781         {
62782             xtype: 'ColumnModel',
62783             xns: Roo.grid,
62784             dataIndex : 'weekday6',
62785             header : 'Saturday',
62786             renderer : cellRender
62787         }
62788     ]);
62789     this.cm = this.colModel;
62790     this.cm.xmodule = this.xmodule || false;
62791  
62792         
62793           
62794     //this.selModel = new Roo.grid.CellSelectionModel();
62795     //this.sm = this.selModel;
62796     //this.selModel.init(this);
62797     
62798     
62799     if(this.width){
62800         this.container.setWidth(this.width);
62801     }
62802
62803     if(this.height){
62804         this.container.setHeight(this.height);
62805     }
62806     /** @private */
62807         this.addEvents({
62808         // raw events
62809         /**
62810          * @event click
62811          * The raw click event for the entire grid.
62812          * @param {Roo.EventObject} e
62813          */
62814         "click" : true,
62815         /**
62816          * @event dblclick
62817          * The raw dblclick event for the entire grid.
62818          * @param {Roo.EventObject} e
62819          */
62820         "dblclick" : true,
62821         /**
62822          * @event contextmenu
62823          * The raw contextmenu event for the entire grid.
62824          * @param {Roo.EventObject} e
62825          */
62826         "contextmenu" : true,
62827         /**
62828          * @event mousedown
62829          * The raw mousedown event for the entire grid.
62830          * @param {Roo.EventObject} e
62831          */
62832         "mousedown" : true,
62833         /**
62834          * @event mouseup
62835          * The raw mouseup event for the entire grid.
62836          * @param {Roo.EventObject} e
62837          */
62838         "mouseup" : true,
62839         /**
62840          * @event mouseover
62841          * The raw mouseover event for the entire grid.
62842          * @param {Roo.EventObject} e
62843          */
62844         "mouseover" : true,
62845         /**
62846          * @event mouseout
62847          * The raw mouseout event for the entire grid.
62848          * @param {Roo.EventObject} e
62849          */
62850         "mouseout" : true,
62851         /**
62852          * @event keypress
62853          * The raw keypress event for the entire grid.
62854          * @param {Roo.EventObject} e
62855          */
62856         "keypress" : true,
62857         /**
62858          * @event keydown
62859          * The raw keydown event for the entire grid.
62860          * @param {Roo.EventObject} e
62861          */
62862         "keydown" : true,
62863
62864         // custom events
62865
62866         /**
62867          * @event cellclick
62868          * Fires when a cell is clicked
62869          * @param {Grid} this
62870          * @param {Number} rowIndex
62871          * @param {Number} columnIndex
62872          * @param {Roo.EventObject} e
62873          */
62874         "cellclick" : true,
62875         /**
62876          * @event celldblclick
62877          * Fires when a cell is double clicked
62878          * @param {Grid} this
62879          * @param {Number} rowIndex
62880          * @param {Number} columnIndex
62881          * @param {Roo.EventObject} e
62882          */
62883         "celldblclick" : true,
62884         /**
62885          * @event rowclick
62886          * Fires when a row is clicked
62887          * @param {Grid} this
62888          * @param {Number} rowIndex
62889          * @param {Roo.EventObject} e
62890          */
62891         "rowclick" : true,
62892         /**
62893          * @event rowdblclick
62894          * Fires when a row is double clicked
62895          * @param {Grid} this
62896          * @param {Number} rowIndex
62897          * @param {Roo.EventObject} e
62898          */
62899         "rowdblclick" : true,
62900         /**
62901          * @event headerclick
62902          * Fires when a header is clicked
62903          * @param {Grid} this
62904          * @param {Number} columnIndex
62905          * @param {Roo.EventObject} e
62906          */
62907         "headerclick" : true,
62908         /**
62909          * @event headerdblclick
62910          * Fires when a header cell is double clicked
62911          * @param {Grid} this
62912          * @param {Number} columnIndex
62913          * @param {Roo.EventObject} e
62914          */
62915         "headerdblclick" : true,
62916         /**
62917          * @event rowcontextmenu
62918          * Fires when a row is right clicked
62919          * @param {Grid} this
62920          * @param {Number} rowIndex
62921          * @param {Roo.EventObject} e
62922          */
62923         "rowcontextmenu" : true,
62924         /**
62925          * @event cellcontextmenu
62926          * Fires when a cell is right clicked
62927          * @param {Grid} this
62928          * @param {Number} rowIndex
62929          * @param {Number} cellIndex
62930          * @param {Roo.EventObject} e
62931          */
62932          "cellcontextmenu" : true,
62933         /**
62934          * @event headercontextmenu
62935          * Fires when a header is right clicked
62936          * @param {Grid} this
62937          * @param {Number} columnIndex
62938          * @param {Roo.EventObject} e
62939          */
62940         "headercontextmenu" : true,
62941         /**
62942          * @event bodyscroll
62943          * Fires when the body element is scrolled
62944          * @param {Number} scrollLeft
62945          * @param {Number} scrollTop
62946          */
62947         "bodyscroll" : true,
62948         /**
62949          * @event columnresize
62950          * Fires when the user resizes a column
62951          * @param {Number} columnIndex
62952          * @param {Number} newSize
62953          */
62954         "columnresize" : true,
62955         /**
62956          * @event columnmove
62957          * Fires when the user moves a column
62958          * @param {Number} oldIndex
62959          * @param {Number} newIndex
62960          */
62961         "columnmove" : true,
62962         /**
62963          * @event startdrag
62964          * Fires when row(s) start being dragged
62965          * @param {Grid} this
62966          * @param {Roo.GridDD} dd The drag drop object
62967          * @param {event} e The raw browser event
62968          */
62969         "startdrag" : true,
62970         /**
62971          * @event enddrag
62972          * Fires when a drag operation is complete
62973          * @param {Grid} this
62974          * @param {Roo.GridDD} dd The drag drop object
62975          * @param {event} e The raw browser event
62976          */
62977         "enddrag" : true,
62978         /**
62979          * @event dragdrop
62980          * Fires when dragged row(s) are dropped on a valid DD target
62981          * @param {Grid} this
62982          * @param {Roo.GridDD} dd The drag drop object
62983          * @param {String} targetId The target drag drop object
62984          * @param {event} e The raw browser event
62985          */
62986         "dragdrop" : true,
62987         /**
62988          * @event dragover
62989          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62990          * @param {Grid} this
62991          * @param {Roo.GridDD} dd The drag drop object
62992          * @param {String} targetId The target drag drop object
62993          * @param {event} e The raw browser event
62994          */
62995         "dragover" : true,
62996         /**
62997          * @event dragenter
62998          *  Fires when the dragged row(s) first cross another DD target while being dragged
62999          * @param {Grid} this
63000          * @param {Roo.GridDD} dd The drag drop object
63001          * @param {String} targetId The target drag drop object
63002          * @param {event} e The raw browser event
63003          */
63004         "dragenter" : true,
63005         /**
63006          * @event dragout
63007          * Fires when the dragged row(s) leave another DD target while being dragged
63008          * @param {Grid} this
63009          * @param {Roo.GridDD} dd The drag drop object
63010          * @param {String} targetId The target drag drop object
63011          * @param {event} e The raw browser event
63012          */
63013         "dragout" : true,
63014         /**
63015          * @event rowclass
63016          * Fires when a row is rendered, so you can change add a style to it.
63017          * @param {GridView} gridview   The grid view
63018          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63019          */
63020         'rowclass' : true,
63021
63022         /**
63023          * @event render
63024          * Fires when the grid is rendered
63025          * @param {Grid} grid
63026          */
63027         'render' : true,
63028             /**
63029              * @event select
63030              * Fires when a date is selected
63031              * @param {DatePicker} this
63032              * @param {Date} date The selected date
63033              */
63034         'select': true,
63035         /**
63036              * @event monthchange
63037              * Fires when the displayed month changes 
63038              * @param {DatePicker} this
63039              * @param {Date} date The selected month
63040              */
63041         'monthchange': true,
63042         /**
63043              * @event evententer
63044              * Fires when mouse over an event
63045              * @param {Calendar} this
63046              * @param {event} Event
63047              */
63048         'evententer': true,
63049         /**
63050              * @event eventleave
63051              * Fires when the mouse leaves an
63052              * @param {Calendar} this
63053              * @param {event}
63054              */
63055         'eventleave': true,
63056         /**
63057              * @event eventclick
63058              * Fires when the mouse click an
63059              * @param {Calendar} this
63060              * @param {event}
63061              */
63062         'eventclick': true,
63063         /**
63064              * @event eventrender
63065              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63066              * @param {Calendar} this
63067              * @param {data} data to be modified
63068              */
63069         'eventrender': true
63070         
63071     });
63072
63073     Roo.grid.Grid.superclass.constructor.call(this);
63074     this.on('render', function() {
63075         this.view.el.addClass('x-grid-cal'); 
63076         
63077         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63078
63079     },this);
63080     
63081     if (!Roo.grid.Calendar.style) {
63082         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63083             
63084             
63085             '.x-grid-cal .x-grid-col' :  {
63086                 height: 'auto !important',
63087                 'vertical-align': 'top'
63088             },
63089             '.x-grid-cal  .fc-event-hori' : {
63090                 height: '14px'
63091             }
63092              
63093             
63094         }, Roo.id());
63095     }
63096
63097     
63098     
63099 };
63100 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63101     /**
63102      * @cfg {Store} eventStore The store that loads events.
63103      */
63104     eventStore : 25,
63105
63106      
63107     activeDate : false,
63108     startDay : 0,
63109     autoWidth : true,
63110     monitorWindowResize : false,
63111
63112     
63113     resizeColumns : function() {
63114         var col = (this.view.el.getWidth() / 7) - 3;
63115         // loop through cols, and setWidth
63116         for(var i =0 ; i < 7 ; i++){
63117             this.cm.setColumnWidth(i, col);
63118         }
63119     },
63120      setDate :function(date) {
63121         
63122         Roo.log('setDate?');
63123         
63124         this.resizeColumns();
63125         var vd = this.activeDate;
63126         this.activeDate = date;
63127 //        if(vd && this.el){
63128 //            var t = date.getTime();
63129 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63130 //                Roo.log('using add remove');
63131 //                
63132 //                this.fireEvent('monthchange', this, date);
63133 //                
63134 //                this.cells.removeClass("fc-state-highlight");
63135 //                this.cells.each(function(c){
63136 //                   if(c.dateValue == t){
63137 //                       c.addClass("fc-state-highlight");
63138 //                       setTimeout(function(){
63139 //                            try{c.dom.firstChild.focus();}catch(e){}
63140 //                       }, 50);
63141 //                       return false;
63142 //                   }
63143 //                   return true;
63144 //                });
63145 //                return;
63146 //            }
63147 //        }
63148         
63149         var days = date.getDaysInMonth();
63150         
63151         var firstOfMonth = date.getFirstDateOfMonth();
63152         var startingPos = firstOfMonth.getDay()-this.startDay;
63153         
63154         if(startingPos < this.startDay){
63155             startingPos += 7;
63156         }
63157         
63158         var pm = date.add(Date.MONTH, -1);
63159         var prevStart = pm.getDaysInMonth()-startingPos;
63160 //        
63161         
63162         
63163         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63164         
63165         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63166         //this.cells.addClassOnOver('fc-state-hover');
63167         
63168         var cells = this.cells.elements;
63169         var textEls = this.textNodes;
63170         
63171         //Roo.each(cells, function(cell){
63172         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63173         //});
63174         
63175         days += startingPos;
63176
63177         // convert everything to numbers so it's fast
63178         var day = 86400000;
63179         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63180         //Roo.log(d);
63181         //Roo.log(pm);
63182         //Roo.log(prevStart);
63183         
63184         var today = new Date().clearTime().getTime();
63185         var sel = date.clearTime().getTime();
63186         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63187         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63188         var ddMatch = this.disabledDatesRE;
63189         var ddText = this.disabledDatesText;
63190         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63191         var ddaysText = this.disabledDaysText;
63192         var format = this.format;
63193         
63194         var setCellClass = function(cal, cell){
63195             
63196             //Roo.log('set Cell Class');
63197             cell.title = "";
63198             var t = d.getTime();
63199             
63200             //Roo.log(d);
63201             
63202             
63203             cell.dateValue = t;
63204             if(t == today){
63205                 cell.className += " fc-today";
63206                 cell.className += " fc-state-highlight";
63207                 cell.title = cal.todayText;
63208             }
63209             if(t == sel){
63210                 // disable highlight in other month..
63211                 cell.className += " fc-state-highlight";
63212                 
63213             }
63214             // disabling
63215             if(t < min) {
63216                 //cell.className = " fc-state-disabled";
63217                 cell.title = cal.minText;
63218                 return;
63219             }
63220             if(t > max) {
63221                 //cell.className = " fc-state-disabled";
63222                 cell.title = cal.maxText;
63223                 return;
63224             }
63225             if(ddays){
63226                 if(ddays.indexOf(d.getDay()) != -1){
63227                     // cell.title = ddaysText;
63228                    // cell.className = " fc-state-disabled";
63229                 }
63230             }
63231             if(ddMatch && format){
63232                 var fvalue = d.dateFormat(format);
63233                 if(ddMatch.test(fvalue)){
63234                     cell.title = ddText.replace("%0", fvalue);
63235                    cell.className = " fc-state-disabled";
63236                 }
63237             }
63238             
63239             if (!cell.initialClassName) {
63240                 cell.initialClassName = cell.dom.className;
63241             }
63242             
63243             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63244         };
63245
63246         var i = 0;
63247         
63248         for(; i < startingPos; i++) {
63249             cells[i].dayName =  (++prevStart);
63250             Roo.log(textEls[i]);
63251             d.setDate(d.getDate()+1);
63252             
63253             //cells[i].className = "fc-past fc-other-month";
63254             setCellClass(this, cells[i]);
63255         }
63256         
63257         var intDay = 0;
63258         
63259         for(; i < days; i++){
63260             intDay = i - startingPos + 1;
63261             cells[i].dayName =  (intDay);
63262             d.setDate(d.getDate()+1);
63263             
63264             cells[i].className = ''; // "x-date-active";
63265             setCellClass(this, cells[i]);
63266         }
63267         var extraDays = 0;
63268         
63269         for(; i < 42; i++) {
63270             //textEls[i].innerHTML = (++extraDays);
63271             
63272             d.setDate(d.getDate()+1);
63273             cells[i].dayName = (++extraDays);
63274             cells[i].className = "fc-future fc-other-month";
63275             setCellClass(this, cells[i]);
63276         }
63277         
63278         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63279         
63280         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63281         
63282         // this will cause all the cells to mis
63283         var rows= [];
63284         var i =0;
63285         for (var r = 0;r < 6;r++) {
63286             for (var c =0;c < 7;c++) {
63287                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63288             }    
63289         }
63290         
63291         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63292         for(i=0;i<cells.length;i++) {
63293             
63294             this.cells.elements[i].dayName = cells[i].dayName ;
63295             this.cells.elements[i].className = cells[i].className;
63296             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63297             this.cells.elements[i].title = cells[i].title ;
63298             this.cells.elements[i].dateValue = cells[i].dateValue ;
63299         }
63300         
63301         
63302         
63303         
63304         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63305         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63306         
63307         ////if(totalRows != 6){
63308             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63309            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63310        // }
63311         
63312         this.fireEvent('monthchange', this, date);
63313         
63314         
63315     },
63316  /**
63317      * Returns the grid's SelectionModel.
63318      * @return {SelectionModel}
63319      */
63320     getSelectionModel : function(){
63321         if(!this.selModel){
63322             this.selModel = new Roo.grid.CellSelectionModel();
63323         }
63324         return this.selModel;
63325     },
63326
63327     load: function() {
63328         this.eventStore.load()
63329         
63330         
63331         
63332     },
63333     
63334     findCell : function(dt) {
63335         dt = dt.clearTime().getTime();
63336         var ret = false;
63337         this.cells.each(function(c){
63338             //Roo.log("check " +c.dateValue + '?=' + dt);
63339             if(c.dateValue == dt){
63340                 ret = c;
63341                 return false;
63342             }
63343             return true;
63344         });
63345         
63346         return ret;
63347     },
63348     
63349     findCells : function(rec) {
63350         var s = rec.data.start_dt.clone().clearTime().getTime();
63351        // Roo.log(s);
63352         var e= rec.data.end_dt.clone().clearTime().getTime();
63353        // Roo.log(e);
63354         var ret = [];
63355         this.cells.each(function(c){
63356              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63357             
63358             if(c.dateValue > e){
63359                 return ;
63360             }
63361             if(c.dateValue < s){
63362                 return ;
63363             }
63364             ret.push(c);
63365         });
63366         
63367         return ret;    
63368     },
63369     
63370     findBestRow: function(cells)
63371     {
63372         var ret = 0;
63373         
63374         for (var i =0 ; i < cells.length;i++) {
63375             ret  = Math.max(cells[i].rows || 0,ret);
63376         }
63377         return ret;
63378         
63379     },
63380     
63381     
63382     addItem : function(rec)
63383     {
63384         // look for vertical location slot in
63385         var cells = this.findCells(rec);
63386         
63387         rec.row = this.findBestRow(cells);
63388         
63389         // work out the location.
63390         
63391         var crow = false;
63392         var rows = [];
63393         for(var i =0; i < cells.length; i++) {
63394             if (!crow) {
63395                 crow = {
63396                     start : cells[i],
63397                     end :  cells[i]
63398                 };
63399                 continue;
63400             }
63401             if (crow.start.getY() == cells[i].getY()) {
63402                 // on same row.
63403                 crow.end = cells[i];
63404                 continue;
63405             }
63406             // different row.
63407             rows.push(crow);
63408             crow = {
63409                 start: cells[i],
63410                 end : cells[i]
63411             };
63412             
63413         }
63414         
63415         rows.push(crow);
63416         rec.els = [];
63417         rec.rows = rows;
63418         rec.cells = cells;
63419         for (var i = 0; i < cells.length;i++) {
63420             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63421             
63422         }
63423         
63424         
63425     },
63426     
63427     clearEvents: function() {
63428         
63429         if (!this.eventStore.getCount()) {
63430             return;
63431         }
63432         // reset number of rows in cells.
63433         Roo.each(this.cells.elements, function(c){
63434             c.rows = 0;
63435         });
63436         
63437         this.eventStore.each(function(e) {
63438             this.clearEvent(e);
63439         },this);
63440         
63441     },
63442     
63443     clearEvent : function(ev)
63444     {
63445         if (ev.els) {
63446             Roo.each(ev.els, function(el) {
63447                 el.un('mouseenter' ,this.onEventEnter, this);
63448                 el.un('mouseleave' ,this.onEventLeave, this);
63449                 el.remove();
63450             },this);
63451             ev.els = [];
63452         }
63453     },
63454     
63455     
63456     renderEvent : function(ev,ctr) {
63457         if (!ctr) {
63458              ctr = this.view.el.select('.fc-event-container',true).first();
63459         }
63460         
63461          
63462         this.clearEvent(ev);
63463             //code
63464        
63465         
63466         
63467         ev.els = [];
63468         var cells = ev.cells;
63469         var rows = ev.rows;
63470         this.fireEvent('eventrender', this, ev);
63471         
63472         for(var i =0; i < rows.length; i++) {
63473             
63474             cls = '';
63475             if (i == 0) {
63476                 cls += ' fc-event-start';
63477             }
63478             if ((i+1) == rows.length) {
63479                 cls += ' fc-event-end';
63480             }
63481             
63482             //Roo.log(ev.data);
63483             // how many rows should it span..
63484             var cg = this.eventTmpl.append(ctr,Roo.apply({
63485                 fccls : cls
63486                 
63487             }, ev.data) , true);
63488             
63489             
63490             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63491             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63492             cg.on('click', this.onEventClick, this, ev);
63493             
63494             ev.els.push(cg);
63495             
63496             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63497             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63498             //Roo.log(cg);
63499              
63500             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63501             cg.setWidth(ebox.right - sbox.x -2);
63502         }
63503     },
63504     
63505     renderEvents: function()
63506     {   
63507         // first make sure there is enough space..
63508         
63509         if (!this.eventTmpl) {
63510             this.eventTmpl = new Roo.Template(
63511                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63512                     '<div class="fc-event-inner">' +
63513                         '<span class="fc-event-time">{time}</span>' +
63514                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63515                     '</div>' +
63516                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63517                 '</div>'
63518             );
63519                 
63520         }
63521                
63522         
63523         
63524         this.cells.each(function(c) {
63525             //Roo.log(c.select('.fc-day-content div',true).first());
63526             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63527         });
63528         
63529         var ctr = this.view.el.select('.fc-event-container',true).first();
63530         
63531         var cls;
63532         this.eventStore.each(function(ev){
63533             
63534             this.renderEvent(ev);
63535              
63536              
63537         }, this);
63538         this.view.layout();
63539         
63540     },
63541     
63542     onEventEnter: function (e, el,event,d) {
63543         this.fireEvent('evententer', this, el, event);
63544     },
63545     
63546     onEventLeave: function (e, el,event,d) {
63547         this.fireEvent('eventleave', this, el, event);
63548     },
63549     
63550     onEventClick: function (e, el,event,d) {
63551         this.fireEvent('eventclick', this, el, event);
63552     },
63553     
63554     onMonthChange: function () {
63555         this.store.load();
63556     },
63557     
63558     onLoad: function () {
63559         
63560         //Roo.log('calendar onload');
63561 //         
63562         if(this.eventStore.getCount() > 0){
63563             
63564            
63565             
63566             this.eventStore.each(function(d){
63567                 
63568                 
63569                 // FIXME..
63570                 var add =   d.data;
63571                 if (typeof(add.end_dt) == 'undefined')  {
63572                     Roo.log("Missing End time in calendar data: ");
63573                     Roo.log(d);
63574                     return;
63575                 }
63576                 if (typeof(add.start_dt) == 'undefined')  {
63577                     Roo.log("Missing Start time in calendar data: ");
63578                     Roo.log(d);
63579                     return;
63580                 }
63581                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63582                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63583                 add.id = add.id || d.id;
63584                 add.title = add.title || '??';
63585                 
63586                 this.addItem(d);
63587                 
63588              
63589             },this);
63590         }
63591         
63592         this.renderEvents();
63593     }
63594     
63595
63596 });
63597 /*
63598  grid : {
63599                 xtype: 'Grid',
63600                 xns: Roo.grid,
63601                 listeners : {
63602                     render : function ()
63603                     {
63604                         _this.grid = this;
63605                         
63606                         if (!this.view.el.hasClass('course-timesheet')) {
63607                             this.view.el.addClass('course-timesheet');
63608                         }
63609                         if (this.tsStyle) {
63610                             this.ds.load({});
63611                             return; 
63612                         }
63613                         Roo.log('width');
63614                         Roo.log(_this.grid.view.el.getWidth());
63615                         
63616                         
63617                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63618                             '.course-timesheet .x-grid-row' : {
63619                                 height: '80px'
63620                             },
63621                             '.x-grid-row td' : {
63622                                 'vertical-align' : 0
63623                             },
63624                             '.course-edit-link' : {
63625                                 'color' : 'blue',
63626                                 'text-overflow' : 'ellipsis',
63627                                 'overflow' : 'hidden',
63628                                 'white-space' : 'nowrap',
63629                                 'cursor' : 'pointer'
63630                             },
63631                             '.sub-link' : {
63632                                 'color' : 'green'
63633                             },
63634                             '.de-act-sup-link' : {
63635                                 'color' : 'purple',
63636                                 'text-decoration' : 'line-through'
63637                             },
63638                             '.de-act-link' : {
63639                                 'color' : 'red',
63640                                 'text-decoration' : 'line-through'
63641                             },
63642                             '.course-timesheet .course-highlight' : {
63643                                 'border-top-style': 'dashed !important',
63644                                 'border-bottom-bottom': 'dashed !important'
63645                             },
63646                             '.course-timesheet .course-item' : {
63647                                 'font-family'   : 'tahoma, arial, helvetica',
63648                                 'font-size'     : '11px',
63649                                 'overflow'      : 'hidden',
63650                                 'padding-left'  : '10px',
63651                                 'padding-right' : '10px',
63652                                 'padding-top' : '10px' 
63653                             }
63654                             
63655                         }, Roo.id());
63656                                 this.ds.load({});
63657                     }
63658                 },
63659                 autoWidth : true,
63660                 monitorWindowResize : false,
63661                 cellrenderer : function(v,x,r)
63662                 {
63663                     return v;
63664                 },
63665                 sm : {
63666                     xtype: 'CellSelectionModel',
63667                     xns: Roo.grid
63668                 },
63669                 dataSource : {
63670                     xtype: 'Store',
63671                     xns: Roo.data,
63672                     listeners : {
63673                         beforeload : function (_self, options)
63674                         {
63675                             options.params = options.params || {};
63676                             options.params._month = _this.monthField.getValue();
63677                             options.params.limit = 9999;
63678                             options.params['sort'] = 'when_dt';    
63679                             options.params['dir'] = 'ASC';    
63680                             this.proxy.loadResponse = this.loadResponse;
63681                             Roo.log("load?");
63682                             //this.addColumns();
63683                         },
63684                         load : function (_self, records, options)
63685                         {
63686                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63687                                 // if you click on the translation.. you can edit it...
63688                                 var el = Roo.get(this);
63689                                 var id = el.dom.getAttribute('data-id');
63690                                 var d = el.dom.getAttribute('data-date');
63691                                 var t = el.dom.getAttribute('data-time');
63692                                 //var id = this.child('span').dom.textContent;
63693                                 
63694                                 //Roo.log(this);
63695                                 Pman.Dialog.CourseCalendar.show({
63696                                     id : id,
63697                                     when_d : d,
63698                                     when_t : t,
63699                                     productitem_active : id ? 1 : 0
63700                                 }, function() {
63701                                     _this.grid.ds.load({});
63702                                 });
63703                            
63704                            });
63705                            
63706                            _this.panel.fireEvent('resize', [ '', '' ]);
63707                         }
63708                     },
63709                     loadResponse : function(o, success, response){
63710                             // this is overridden on before load..
63711                             
63712                             Roo.log("our code?");       
63713                             //Roo.log(success);
63714                             //Roo.log(response)
63715                             delete this.activeRequest;
63716                             if(!success){
63717                                 this.fireEvent("loadexception", this, o, response);
63718                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63719                                 return;
63720                             }
63721                             var result;
63722                             try {
63723                                 result = o.reader.read(response);
63724                             }catch(e){
63725                                 Roo.log("load exception?");
63726                                 this.fireEvent("loadexception", this, o, response, e);
63727                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63728                                 return;
63729                             }
63730                             Roo.log("ready...");        
63731                             // loop through result.records;
63732                             // and set this.tdate[date] = [] << array of records..
63733                             _this.tdata  = {};
63734                             Roo.each(result.records, function(r){
63735                                 //Roo.log(r.data);
63736                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63737                                     _this.tdata[r.data.when_dt.format('j')] = [];
63738                                 }
63739                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63740                             });
63741                             
63742                             //Roo.log(_this.tdata);
63743                             
63744                             result.records = [];
63745                             result.totalRecords = 6;
63746                     
63747                             // let's generate some duumy records for the rows.
63748                             //var st = _this.dateField.getValue();
63749                             
63750                             // work out monday..
63751                             //st = st.add(Date.DAY, -1 * st.format('w'));
63752                             
63753                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63754                             
63755                             var firstOfMonth = date.getFirstDayOfMonth();
63756                             var days = date.getDaysInMonth();
63757                             var d = 1;
63758                             var firstAdded = false;
63759                             for (var i = 0; i < result.totalRecords ; i++) {
63760                                 //var d= st.add(Date.DAY, i);
63761                                 var row = {};
63762                                 var added = 0;
63763                                 for(var w = 0 ; w < 7 ; w++){
63764                                     if(!firstAdded && firstOfMonth != w){
63765                                         continue;
63766                                     }
63767                                     if(d > days){
63768                                         continue;
63769                                     }
63770                                     firstAdded = true;
63771                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63772                                     row['weekday'+w] = String.format(
63773                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63774                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63775                                                     d,
63776                                                     date.format('Y-m-')+dd
63777                                                 );
63778                                     added++;
63779                                     if(typeof(_this.tdata[d]) != 'undefined'){
63780                                         Roo.each(_this.tdata[d], function(r){
63781                                             var is_sub = '';
63782                                             var deactive = '';
63783                                             var id = r.id;
63784                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63785                                             if(r.parent_id*1>0){
63786                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63787                                                 id = r.parent_id;
63788                                             }
63789                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63790                                                 deactive = 'de-act-link';
63791                                             }
63792                                             
63793                                             row['weekday'+w] += String.format(
63794                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63795                                                     id, //0
63796                                                     r.product_id_name, //1
63797                                                     r.when_dt.format('h:ia'), //2
63798                                                     is_sub, //3
63799                                                     deactive, //4
63800                                                     desc // 5
63801                                             );
63802                                         });
63803                                     }
63804                                     d++;
63805                                 }
63806                                 
63807                                 // only do this if something added..
63808                                 if(added > 0){ 
63809                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63810                                 }
63811                                 
63812                                 
63813                                 // push it twice. (second one with an hour..
63814                                 
63815                             }
63816                             //Roo.log(result);
63817                             this.fireEvent("load", this, o, o.request.arg);
63818                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63819                         },
63820                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63821                     proxy : {
63822                         xtype: 'HttpProxy',
63823                         xns: Roo.data,
63824                         method : 'GET',
63825                         url : baseURL + '/Roo/Shop_course.php'
63826                     },
63827                     reader : {
63828                         xtype: 'JsonReader',
63829                         xns: Roo.data,
63830                         id : 'id',
63831                         fields : [
63832                             {
63833                                 'name': 'id',
63834                                 'type': 'int'
63835                             },
63836                             {
63837                                 'name': 'when_dt',
63838                                 'type': 'string'
63839                             },
63840                             {
63841                                 'name': 'end_dt',
63842                                 'type': 'string'
63843                             },
63844                             {
63845                                 'name': 'parent_id',
63846                                 'type': 'int'
63847                             },
63848                             {
63849                                 'name': 'product_id',
63850                                 'type': 'int'
63851                             },
63852                             {
63853                                 'name': 'productitem_id',
63854                                 'type': 'int'
63855                             },
63856                             {
63857                                 'name': 'guid',
63858                                 'type': 'int'
63859                             }
63860                         ]
63861                     }
63862                 },
63863                 toolbar : {
63864                     xtype: 'Toolbar',
63865                     xns: Roo,
63866                     items : [
63867                         {
63868                             xtype: 'Button',
63869                             xns: Roo.Toolbar,
63870                             listeners : {
63871                                 click : function (_self, e)
63872                                 {
63873                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63874                                     sd.setMonth(sd.getMonth()-1);
63875                                     _this.monthField.setValue(sd.format('Y-m-d'));
63876                                     _this.grid.ds.load({});
63877                                 }
63878                             },
63879                             text : "Back"
63880                         },
63881                         {
63882                             xtype: 'Separator',
63883                             xns: Roo.Toolbar
63884                         },
63885                         {
63886                             xtype: 'MonthField',
63887                             xns: Roo.form,
63888                             listeners : {
63889                                 render : function (_self)
63890                                 {
63891                                     _this.monthField = _self;
63892                                    // _this.monthField.set  today
63893                                 },
63894                                 select : function (combo, date)
63895                                 {
63896                                     _this.grid.ds.load({});
63897                                 }
63898                             },
63899                             value : (function() { return new Date(); })()
63900                         },
63901                         {
63902                             xtype: 'Separator',
63903                             xns: Roo.Toolbar
63904                         },
63905                         {
63906                             xtype: 'TextItem',
63907                             xns: Roo.Toolbar,
63908                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63909                         },
63910                         {
63911                             xtype: 'Fill',
63912                             xns: Roo.Toolbar
63913                         },
63914                         {
63915                             xtype: 'Button',
63916                             xns: Roo.Toolbar,
63917                             listeners : {
63918                                 click : function (_self, e)
63919                                 {
63920                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63921                                     sd.setMonth(sd.getMonth()+1);
63922                                     _this.monthField.setValue(sd.format('Y-m-d'));
63923                                     _this.grid.ds.load({});
63924                                 }
63925                             },
63926                             text : "Next"
63927                         }
63928                     ]
63929                 },
63930                  
63931             }
63932         };
63933         
63934         *//*
63935  * Based on:
63936  * Ext JS Library 1.1.1
63937  * Copyright(c) 2006-2007, Ext JS, LLC.
63938  *
63939  * Originally Released Under LGPL - original licence link has changed is not relivant.
63940  *
63941  * Fork - LGPL
63942  * <script type="text/javascript">
63943  */
63944  
63945 /**
63946  * @class Roo.LoadMask
63947  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63948  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63949  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63950  * element's UpdateManager load indicator and will be destroyed after the initial load.
63951  * @constructor
63952  * Create a new LoadMask
63953  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63954  * @param {Object} config The config object
63955  */
63956 Roo.LoadMask = function(el, config){
63957     this.el = Roo.get(el);
63958     Roo.apply(this, config);
63959     if(this.store){
63960         this.store.on('beforeload', this.onBeforeLoad, this);
63961         this.store.on('load', this.onLoad, this);
63962         this.store.on('loadexception', this.onLoadException, this);
63963         this.removeMask = false;
63964     }else{
63965         var um = this.el.getUpdateManager();
63966         um.showLoadIndicator = false; // disable the default indicator
63967         um.on('beforeupdate', this.onBeforeLoad, this);
63968         um.on('update', this.onLoad, this);
63969         um.on('failure', this.onLoad, this);
63970         this.removeMask = true;
63971     }
63972 };
63973
63974 Roo.LoadMask.prototype = {
63975     /**
63976      * @cfg {Boolean} removeMask
63977      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63978      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63979      */
63980     removeMask : false,
63981     /**
63982      * @cfg {String} msg
63983      * The text to display in a centered loading message box (defaults to 'Loading...')
63984      */
63985     msg : 'Loading...',
63986     /**
63987      * @cfg {String} msgCls
63988      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
63989      */
63990     msgCls : 'x-mask-loading',
63991
63992     /**
63993      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
63994      * @type Boolean
63995      */
63996     disabled: false,
63997
63998     /**
63999      * Disables the mask to prevent it from being displayed
64000      */
64001     disable : function(){
64002        this.disabled = true;
64003     },
64004
64005     /**
64006      * Enables the mask so that it can be displayed
64007      */
64008     enable : function(){
64009         this.disabled = false;
64010     },
64011     
64012     onLoadException : function()
64013     {
64014         Roo.log(arguments);
64015         
64016         if (typeof(arguments[3]) != 'undefined') {
64017             Roo.MessageBox.alert("Error loading",arguments[3]);
64018         } 
64019         /*
64020         try {
64021             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64022                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64023             }   
64024         } catch(e) {
64025             
64026         }
64027         */
64028     
64029         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64030     },
64031     // private
64032     onLoad : function()
64033     {
64034         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64035     },
64036
64037     // private
64038     onBeforeLoad : function(){
64039         if(!this.disabled){
64040             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64041         }
64042     },
64043
64044     // private
64045     destroy : function(){
64046         if(this.store){
64047             this.store.un('beforeload', this.onBeforeLoad, this);
64048             this.store.un('load', this.onLoad, this);
64049             this.store.un('loadexception', this.onLoadException, this);
64050         }else{
64051             var um = this.el.getUpdateManager();
64052             um.un('beforeupdate', this.onBeforeLoad, this);
64053             um.un('update', this.onLoad, this);
64054             um.un('failure', this.onLoad, this);
64055         }
64056     }
64057 };/*
64058  * Based on:
64059  * Ext JS Library 1.1.1
64060  * Copyright(c) 2006-2007, Ext JS, LLC.
64061  *
64062  * Originally Released Under LGPL - original licence link has changed is not relivant.
64063  *
64064  * Fork - LGPL
64065  * <script type="text/javascript">
64066  */
64067
64068
64069 /**
64070  * @class Roo.XTemplate
64071  * @extends Roo.Template
64072  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64073 <pre><code>
64074 var t = new Roo.XTemplate(
64075         '&lt;select name="{name}"&gt;',
64076                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64077         '&lt;/select&gt;'
64078 );
64079  
64080 // then append, applying the master template values
64081  </code></pre>
64082  *
64083  * Supported features:
64084  *
64085  *  Tags:
64086
64087 <pre><code>
64088       {a_variable} - output encoded.
64089       {a_variable.format:("Y-m-d")} - call a method on the variable
64090       {a_variable:raw} - unencoded output
64091       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64092       {a_variable:this.method_on_template(...)} - call a method on the template object.
64093  
64094 </code></pre>
64095  *  The tpl tag:
64096 <pre><code>
64097         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64098         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64099         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64100         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64101   
64102         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64103         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64104 </code></pre>
64105  *      
64106  */
64107 Roo.XTemplate = function()
64108 {
64109     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64110     if (this.html) {
64111         this.compile();
64112     }
64113 };
64114
64115
64116 Roo.extend(Roo.XTemplate, Roo.Template, {
64117
64118     /**
64119      * The various sub templates
64120      */
64121     tpls : false,
64122     /**
64123      *
64124      * basic tag replacing syntax
64125      * WORD:WORD()
64126      *
64127      * // you can fake an object call by doing this
64128      *  x.t:(test,tesT) 
64129      * 
64130      */
64131     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64132
64133     /**
64134      * compile the template
64135      *
64136      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64137      *
64138      */
64139     compile: function()
64140     {
64141         var s = this.html;
64142      
64143         s = ['<tpl>', s, '</tpl>'].join('');
64144     
64145         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64146             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64147             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64148             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64149             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64150             m,
64151             id     = 0,
64152             tpls   = [];
64153     
64154         while(true == !!(m = s.match(re))){
64155             var forMatch   = m[0].match(nameRe),
64156                 ifMatch   = m[0].match(ifRe),
64157                 execMatch   = m[0].match(execRe),
64158                 namedMatch   = m[0].match(namedRe),
64159                 
64160                 exp  = null, 
64161                 fn   = null,
64162                 exec = null,
64163                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64164                 
64165             if (ifMatch) {
64166                 // if - puts fn into test..
64167                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64168                 if(exp){
64169                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64170                 }
64171             }
64172             
64173             if (execMatch) {
64174                 // exec - calls a function... returns empty if true is  returned.
64175                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64176                 if(exp){
64177                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64178                 }
64179             }
64180             
64181             
64182             if (name) {
64183                 // for = 
64184                 switch(name){
64185                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64186                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64187                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64188                 }
64189             }
64190             var uid = namedMatch ? namedMatch[1] : id;
64191             
64192             
64193             tpls.push({
64194                 id:     namedMatch ? namedMatch[1] : id,
64195                 target: name,
64196                 exec:   exec,
64197                 test:   fn,
64198                 body:   m[1] || ''
64199             });
64200             if (namedMatch) {
64201                 s = s.replace(m[0], '');
64202             } else { 
64203                 s = s.replace(m[0], '{xtpl'+ id + '}');
64204             }
64205             ++id;
64206         }
64207         this.tpls = [];
64208         for(var i = tpls.length-1; i >= 0; --i){
64209             this.compileTpl(tpls[i]);
64210             this.tpls[tpls[i].id] = tpls[i];
64211         }
64212         this.master = tpls[tpls.length-1];
64213         return this;
64214     },
64215     /**
64216      * same as applyTemplate, except it's done to one of the subTemplates
64217      * when using named templates, you can do:
64218      *
64219      * var str = pl.applySubTemplate('your-name', values);
64220      *
64221      * 
64222      * @param {Number} id of the template
64223      * @param {Object} values to apply to template
64224      * @param {Object} parent (normaly the instance of this object)
64225      */
64226     applySubTemplate : function(id, values, parent)
64227     {
64228         
64229         
64230         var t = this.tpls[id];
64231         
64232         
64233         try { 
64234             if(t.test && !t.test.call(this, values, parent)){
64235                 return '';
64236             }
64237         } catch(e) {
64238             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64239             Roo.log(e.toString());
64240             Roo.log(t.test);
64241             return ''
64242         }
64243         try { 
64244             
64245             if(t.exec && t.exec.call(this, values, parent)){
64246                 return '';
64247             }
64248         } catch(e) {
64249             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64250             Roo.log(e.toString());
64251             Roo.log(t.exec);
64252             return ''
64253         }
64254         try {
64255             var vs = t.target ? t.target.call(this, values, parent) : values;
64256             parent = t.target ? values : parent;
64257             if(t.target && vs instanceof Array){
64258                 var buf = [];
64259                 for(var i = 0, len = vs.length; i < len; i++){
64260                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64261                 }
64262                 return buf.join('');
64263             }
64264             return t.compiled.call(this, vs, parent);
64265         } catch (e) {
64266             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64267             Roo.log(e.toString());
64268             Roo.log(t.compiled);
64269             return '';
64270         }
64271     },
64272
64273     compileTpl : function(tpl)
64274     {
64275         var fm = Roo.util.Format;
64276         var useF = this.disableFormats !== true;
64277         var sep = Roo.isGecko ? "+" : ",";
64278         var undef = function(str) {
64279             Roo.log("Property not found :"  + str);
64280             return '';
64281         };
64282         
64283         var fn = function(m, name, format, args)
64284         {
64285             //Roo.log(arguments);
64286             args = args ? args.replace(/\\'/g,"'") : args;
64287             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64288             if (typeof(format) == 'undefined') {
64289                 format= 'htmlEncode';
64290             }
64291             if (format == 'raw' ) {
64292                 format = false;
64293             }
64294             
64295             if(name.substr(0, 4) == 'xtpl'){
64296                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64297             }
64298             
64299             // build an array of options to determine if value is undefined..
64300             
64301             // basically get 'xxxx.yyyy' then do
64302             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64303             //    (function () { Roo.log("Property not found"); return ''; })() :
64304             //    ......
64305             
64306             var udef_ar = [];
64307             var lookfor = '';
64308             Roo.each(name.split('.'), function(st) {
64309                 lookfor += (lookfor.length ? '.': '') + st;
64310                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64311             });
64312             
64313             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64314             
64315             
64316             if(format && useF){
64317                 
64318                 args = args ? ',' + args : "";
64319                  
64320                 if(format.substr(0, 5) != "this."){
64321                     format = "fm." + format + '(';
64322                 }else{
64323                     format = 'this.call("'+ format.substr(5) + '", ';
64324                     args = ", values";
64325                 }
64326                 
64327                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64328             }
64329              
64330             if (args.length) {
64331                 // called with xxyx.yuu:(test,test)
64332                 // change to ()
64333                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64334             }
64335             // raw.. - :raw modifier..
64336             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64337             
64338         };
64339         var body;
64340         // branched to use + in gecko and [].join() in others
64341         if(Roo.isGecko){
64342             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64343                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64344                     "';};};";
64345         }else{
64346             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64347             body.push(tpl.body.replace(/(\r\n|\n)/g,
64348                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64349             body.push("'].join('');};};");
64350             body = body.join('');
64351         }
64352         
64353         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64354        
64355         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64356         eval(body);
64357         
64358         return this;
64359     },
64360
64361     applyTemplate : function(values){
64362         return this.master.compiled.call(this, values, {});
64363         //var s = this.subs;
64364     },
64365
64366     apply : function(){
64367         return this.applyTemplate.apply(this, arguments);
64368     }
64369
64370  });
64371
64372 Roo.XTemplate.from = function(el){
64373     el = Roo.getDom(el);
64374     return new Roo.XTemplate(el.value || el.innerHTML);
64375 };